Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright 2021 Google LLC.
0004  */
0005 
0006 #include <linux/init.h>
0007 #include <linux/highmem.h>
0008 #include <linux/kobject.h>
0009 #include <linux/mm.h>
0010 #include <linux/module.h>
0011 #include <linux/slab.h>
0012 #include <linux/sysfs.h>
0013 #include <linux/vmalloc.h>
0014 
0015 #include "internal.h"
0016 
0017 static int module_extend_max_pages(struct load_info *info, unsigned int extent)
0018 {
0019     struct page **new_pages;
0020 
0021     new_pages = kvmalloc_array(info->max_pages + extent,
0022                    sizeof(info->pages), GFP_KERNEL);
0023     if (!new_pages)
0024         return -ENOMEM;
0025 
0026     memcpy(new_pages, info->pages, info->max_pages * sizeof(info->pages));
0027     kvfree(info->pages);
0028     info->pages = new_pages;
0029     info->max_pages += extent;
0030 
0031     return 0;
0032 }
0033 
0034 static struct page *module_get_next_page(struct load_info *info)
0035 {
0036     struct page *page;
0037     int error;
0038 
0039     if (info->max_pages == info->used_pages) {
0040         error = module_extend_max_pages(info, info->used_pages);
0041         if (error)
0042             return ERR_PTR(error);
0043     }
0044 
0045     page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
0046     if (!page)
0047         return ERR_PTR(-ENOMEM);
0048 
0049     info->pages[info->used_pages++] = page;
0050     return page;
0051 }
0052 
0053 #ifdef CONFIG_MODULE_COMPRESS_GZIP
0054 #include <linux/zlib.h>
0055 #define MODULE_COMPRESSION  gzip
0056 #define MODULE_DECOMPRESS_FN    module_gzip_decompress
0057 
0058 /*
0059  * Calculate length of the header which consists of signature, header
0060  * flags, time stamp and operating system ID (10 bytes total), plus
0061  * an optional filename.
0062  */
0063 static size_t module_gzip_header_len(const u8 *buf, size_t size)
0064 {
0065     const u8 signature[] = { 0x1f, 0x8b, 0x08 };
0066     size_t len = 10;
0067 
0068     if (size < len || memcmp(buf, signature, sizeof(signature)))
0069         return 0;
0070 
0071     if (buf[3] & 0x08) {
0072         do {
0073             /*
0074              * If we can't find the end of the file name we must
0075              * be dealing with a corrupted file.
0076              */
0077             if (len == size)
0078                 return 0;
0079         } while (buf[len++] != '\0');
0080     }
0081 
0082     return len;
0083 }
0084 
0085 static ssize_t module_gzip_decompress(struct load_info *info,
0086                       const void *buf, size_t size)
0087 {
0088     struct z_stream_s s = { 0 };
0089     size_t new_size = 0;
0090     size_t gzip_hdr_len;
0091     ssize_t retval;
0092     int rc;
0093 
0094     gzip_hdr_len = module_gzip_header_len(buf, size);
0095     if (!gzip_hdr_len) {
0096         pr_err("not a gzip compressed module\n");
0097         return -EINVAL;
0098     }
0099 
0100     s.next_in = buf + gzip_hdr_len;
0101     s.avail_in = size - gzip_hdr_len;
0102 
0103     s.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
0104     if (!s.workspace)
0105         return -ENOMEM;
0106 
0107     rc = zlib_inflateInit2(&s, -MAX_WBITS);
0108     if (rc != Z_OK) {
0109         pr_err("failed to initialize decompressor: %d\n", rc);
0110         retval = -EINVAL;
0111         goto out;
0112     }
0113 
0114     do {
0115         struct page *page = module_get_next_page(info);
0116 
0117         if (!page) {
0118             retval = -ENOMEM;
0119             goto out_inflate_end;
0120         }
0121 
0122         s.next_out = kmap_local_page(page);
0123         s.avail_out = PAGE_SIZE;
0124         rc = zlib_inflate(&s, 0);
0125         kunmap_local(s.next_out);
0126 
0127         new_size += PAGE_SIZE - s.avail_out;
0128     } while (rc == Z_OK);
0129 
0130     if (rc != Z_STREAM_END) {
0131         pr_err("decompression failed with status %d\n", rc);
0132         retval = -EINVAL;
0133         goto out_inflate_end;
0134     }
0135 
0136     retval = new_size;
0137 
0138 out_inflate_end:
0139     zlib_inflateEnd(&s);
0140 out:
0141     kfree(s.workspace);
0142     return retval;
0143 }
0144 #elif CONFIG_MODULE_COMPRESS_XZ
0145 #include <linux/xz.h>
0146 #define MODULE_COMPRESSION  xz
0147 #define MODULE_DECOMPRESS_FN    module_xz_decompress
0148 
0149 static ssize_t module_xz_decompress(struct load_info *info,
0150                     const void *buf, size_t size)
0151 {
0152     static const u8 signature[] = { 0xfd, '7', 'z', 'X', 'Z', 0 };
0153     struct xz_dec *xz_dec;
0154     struct xz_buf xz_buf;
0155     enum xz_ret xz_ret;
0156     size_t new_size = 0;
0157     ssize_t retval;
0158 
0159     if (size < sizeof(signature) ||
0160         memcmp(buf, signature, sizeof(signature))) {
0161         pr_err("not an xz compressed module\n");
0162         return -EINVAL;
0163     }
0164 
0165     xz_dec = xz_dec_init(XZ_DYNALLOC, (u32)-1);
0166     if (!xz_dec)
0167         return -ENOMEM;
0168 
0169     xz_buf.in_size = size;
0170     xz_buf.in = buf;
0171     xz_buf.in_pos = 0;
0172 
0173     do {
0174         struct page *page = module_get_next_page(info);
0175 
0176         if (!page) {
0177             retval = -ENOMEM;
0178             goto out;
0179         }
0180 
0181         xz_buf.out = kmap_local_page(page);
0182         xz_buf.out_pos = 0;
0183         xz_buf.out_size = PAGE_SIZE;
0184         xz_ret = xz_dec_run(xz_dec, &xz_buf);
0185         kunmap_local(xz_buf.out);
0186 
0187         new_size += xz_buf.out_pos;
0188     } while (xz_buf.out_pos == PAGE_SIZE && xz_ret == XZ_OK);
0189 
0190     if (xz_ret != XZ_STREAM_END) {
0191         pr_err("decompression failed with status %d\n", xz_ret);
0192         retval = -EINVAL;
0193         goto out;
0194     }
0195 
0196     retval = new_size;
0197 
0198  out:
0199     xz_dec_end(xz_dec);
0200     return retval;
0201 }
0202 #else
0203 #error "Unexpected configuration for CONFIG_MODULE_DECOMPRESS"
0204 #endif
0205 
0206 int module_decompress(struct load_info *info, const void *buf, size_t size)
0207 {
0208     unsigned int n_pages;
0209     ssize_t data_size;
0210     int error;
0211 
0212     /*
0213      * Start with number of pages twice as big as needed for
0214      * compressed data.
0215      */
0216     n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
0217     error = module_extend_max_pages(info, n_pages);
0218 
0219     data_size = MODULE_DECOMPRESS_FN(info, buf, size);
0220     if (data_size < 0) {
0221         error = data_size;
0222         goto err;
0223     }
0224 
0225     info->hdr = vmap(info->pages, info->used_pages, VM_MAP, PAGE_KERNEL);
0226     if (!info->hdr) {
0227         error = -ENOMEM;
0228         goto err;
0229     }
0230 
0231     info->len = data_size;
0232     return 0;
0233 
0234 err:
0235     module_decompress_cleanup(info);
0236     return error;
0237 }
0238 
0239 void module_decompress_cleanup(struct load_info *info)
0240 {
0241     int i;
0242 
0243     if (info->hdr)
0244         vunmap(info->hdr);
0245 
0246     for (i = 0; i < info->used_pages; i++)
0247         __free_page(info->pages[i]);
0248 
0249     kvfree(info->pages);
0250 
0251     info->pages = NULL;
0252     info->max_pages = info->used_pages = 0;
0253 }
0254 
0255 #ifdef CONFIG_SYSFS
0256 static ssize_t compression_show(struct kobject *kobj,
0257                 struct kobj_attribute *attr, char *buf)
0258 {
0259     return sysfs_emit(buf, "%s\n", __stringify(MODULE_COMPRESSION));
0260 }
0261 
0262 static struct kobj_attribute module_compression_attr = __ATTR_RO(compression);
0263 
0264 static int __init module_decompress_sysfs_init(void)
0265 {
0266     int error;
0267 
0268     error = sysfs_create_file(&module_kset->kobj,
0269                   &module_compression_attr.attr);
0270     if (error)
0271         pr_warn("Failed to create 'compression' attribute");
0272 
0273     return 0;
0274 }
0275 late_initcall(module_decompress_sysfs_init);
0276 #endif