1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2021 Google LLC. 4 */ 5 6 #include <linux/init.h> 7 #include <linux/highmem.h> 8 #include <linux/kobject.h> 9 #include <linux/mm.h> 10 #include <linux/module.h> 11 #include <linux/slab.h> 12 #include <linux/sysfs.h> 13 #include <linux/vmalloc.h> 14 15 #include "internal.h" 16 17 static int module_extend_max_pages(struct load_info *info, unsigned int extent) 18 { 19 struct page **new_pages; 20 21 new_pages = kvmalloc_array(info->max_pages + extent, 22 sizeof(info->pages), GFP_KERNEL); 23 if (!new_pages) 24 return -ENOMEM; 25 26 memcpy(new_pages, info->pages, info->max_pages * sizeof(info->pages)); 27 kvfree(info->pages); 28 info->pages = new_pages; 29 info->max_pages += extent; 30 31 return 0; 32 } 33 34 static struct page *module_get_next_page(struct load_info *info) 35 { 36 struct page *page; 37 int error; 38 39 if (info->max_pages == info->used_pages) { 40 error = module_extend_max_pages(info, info->used_pages); 41 if (error) 42 return ERR_PTR(error); 43 } 44 45 page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); 46 if (!page) 47 return ERR_PTR(-ENOMEM); 48 49 info->pages[info->used_pages++] = page; 50 return page; 51 } 52 53 #ifdef CONFIG_MODULE_COMPRESS_GZIP 54 #include <linux/zlib.h> 55 #define MODULE_COMPRESSION gzip 56 #define MODULE_DECOMPRESS_FN module_gzip_decompress 57 58 /* 59 * Calculate length of the header which consists of signature, header 60 * flags, time stamp and operating system ID (10 bytes total), plus 61 * an optional filename. 62 */ 63 static size_t module_gzip_header_len(const u8 *buf, size_t size) 64 { 65 const u8 signature[] = { 0x1f, 0x8b, 0x08 }; 66 size_t len = 10; 67 68 if (size < len || memcmp(buf, signature, sizeof(signature))) 69 return 0; 70 71 if (buf[3] & 0x08) { 72 do { 73 /* 74 * If we can't find the end of the file name we must 75 * be dealing with a corrupted file. 76 */ 77 if (len == size) 78 return 0; 79 } while (buf[len++] != '\0'); 80 } 81 82 return len; 83 } 84 85 static ssize_t module_gzip_decompress(struct load_info *info, 86 const void *buf, size_t size) 87 { 88 struct z_stream_s s = { 0 }; 89 size_t new_size = 0; 90 size_t gzip_hdr_len; 91 ssize_t retval; 92 int rc; 93 94 gzip_hdr_len = module_gzip_header_len(buf, size); 95 if (!gzip_hdr_len) { 96 pr_err("not a gzip compressed module\n"); 97 return -EINVAL; 98 } 99 100 s.next_in = buf + gzip_hdr_len; 101 s.avail_in = size - gzip_hdr_len; 102 103 s.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL); 104 if (!s.workspace) 105 return -ENOMEM; 106 107 rc = zlib_inflateInit2(&s, -MAX_WBITS); 108 if (rc != Z_OK) { 109 pr_err("failed to initialize decompressor: %d\n", rc); 110 retval = -EINVAL; 111 goto out; 112 } 113 114 do { 115 struct page *page = module_get_next_page(info); 116 if (!page) { 117 retval = -ENOMEM; 118 goto out_inflate_end; 119 } 120 121 s.next_out = kmap(page); 122 s.avail_out = PAGE_SIZE; 123 rc = zlib_inflate(&s, 0); 124 kunmap(page); 125 126 new_size += PAGE_SIZE - s.avail_out; 127 } while (rc == Z_OK); 128 129 if (rc != Z_STREAM_END) { 130 pr_err("decompression failed with status %d\n", rc); 131 retval = -EINVAL; 132 goto out_inflate_end; 133 } 134 135 retval = new_size; 136 137 out_inflate_end: 138 zlib_inflateEnd(&s); 139 out: 140 kfree(s.workspace); 141 return retval; 142 } 143 #elif CONFIG_MODULE_COMPRESS_XZ 144 #include <linux/xz.h> 145 #define MODULE_COMPRESSION xz 146 #define MODULE_DECOMPRESS_FN module_xz_decompress 147 148 static ssize_t module_xz_decompress(struct load_info *info, 149 const void *buf, size_t size) 150 { 151 static const u8 signature[] = { 0xfd, '7', 'z', 'X', 'Z', 0 }; 152 struct xz_dec *xz_dec; 153 struct xz_buf xz_buf; 154 enum xz_ret xz_ret; 155 size_t new_size = 0; 156 ssize_t retval; 157 158 if (size < sizeof(signature) || 159 memcmp(buf, signature, sizeof(signature))) { 160 pr_err("not an xz compressed module\n"); 161 return -EINVAL; 162 } 163 164 xz_dec = xz_dec_init(XZ_DYNALLOC, (u32)-1); 165 if (!xz_dec) 166 return -ENOMEM; 167 168 xz_buf.in_size = size; 169 xz_buf.in = buf; 170 xz_buf.in_pos = 0; 171 172 do { 173 struct page *page = module_get_next_page(info); 174 if (!page) { 175 retval = -ENOMEM; 176 goto out; 177 } 178 179 xz_buf.out = kmap(page); 180 xz_buf.out_pos = 0; 181 xz_buf.out_size = PAGE_SIZE; 182 xz_ret = xz_dec_run(xz_dec, &xz_buf); 183 kunmap(page); 184 185 new_size += xz_buf.out_pos; 186 } while (xz_buf.out_pos == PAGE_SIZE && xz_ret == XZ_OK); 187 188 if (xz_ret != XZ_STREAM_END) { 189 pr_err("decompression failed with status %d\n", xz_ret); 190 retval = -EINVAL; 191 goto out; 192 } 193 194 retval = new_size; 195 196 out: 197 xz_dec_end(xz_dec); 198 return retval; 199 } 200 #else 201 #error "Unexpected configuration for CONFIG_MODULE_DECOMPRESS" 202 #endif 203 204 int module_decompress(struct load_info *info, const void *buf, size_t size) 205 { 206 unsigned int n_pages; 207 ssize_t data_size; 208 int error; 209 210 /* 211 * Start with number of pages twice as big as needed for 212 * compressed data. 213 */ 214 n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2; 215 error = module_extend_max_pages(info, n_pages); 216 217 data_size = MODULE_DECOMPRESS_FN(info, buf, size); 218 if (data_size < 0) { 219 error = data_size; 220 goto err; 221 } 222 223 info->hdr = vmap(info->pages, info->used_pages, VM_MAP, PAGE_KERNEL); 224 if (!info->hdr) { 225 error = -ENOMEM; 226 goto err; 227 } 228 229 info->len = data_size; 230 return 0; 231 232 err: 233 module_decompress_cleanup(info); 234 return error; 235 } 236 237 void module_decompress_cleanup(struct load_info *info) 238 { 239 int i; 240 241 if (info->hdr) 242 vunmap(info->hdr); 243 244 for (i = 0; i < info->used_pages; i++) 245 __free_page(info->pages[i]); 246 247 kvfree(info->pages); 248 249 info->pages = NULL; 250 info->max_pages = info->used_pages = 0; 251 } 252 253 #ifdef CONFIG_SYSFS 254 static ssize_t compression_show(struct kobject *kobj, 255 struct kobj_attribute *attr, char *buf) 256 { 257 return sysfs_emit(buf, "%s\n", __stringify(MODULE_COMPRESSION)); 258 } 259 static struct kobj_attribute module_compression_attr = __ATTR_RO(compression); 260 261 static int __init module_decompress_sysfs_init(void) 262 { 263 int error; 264 265 error = sysfs_create_file(&module_kset->kobj, 266 &module_compression_attr.attr); 267 if (error) 268 pr_warn("Failed to create 'compression' attribute"); 269 270 return 0; 271 } 272 late_initcall(module_decompress_sysfs_init); 273 #endif 274