#define _GNU_SOURCE #include #include #include #include #include #include #include #include "kexec.h" #include #ifdef HAVE_LIBZ #include #endif /* * Basic uImage loader. Not rocket science. */ /* * Returns the image type if everything goes well. This would * allow the user to decide if the image is of their interest. * * Returns -1 on a corrupted image * * Returns 0 if this is not a uImage */ int uImage_probe(const unsigned char *buf, off_t len, unsigned int arch) { struct image_header header; #ifdef HAVE_LIBZ unsigned int crc; unsigned int hcrc; #endif if ((uintmax_t)len < (uintmax_t)sizeof(header)) return -1; memcpy(&header, buf, sizeof(header)); if (be32_to_cpu(header.ih_magic) != IH_MAGIC) return 0; #ifdef HAVE_LIBZ hcrc = be32_to_cpu(header.ih_hcrc); header.ih_hcrc = 0; crc = crc32(0, (void *)&header, sizeof(header)); if (crc != hcrc) { printf("Header checksum of the uImage does not match\n"); return -1; } #endif switch (header.ih_type) { case IH_TYPE_KERNEL: case IH_TYPE_KERNEL_NOLOAD: break; case IH_TYPE_RAMDISK: break; default: printf("uImage type %d unsupported\n", header.ih_type); return -1; } if (header.ih_os != IH_OS_LINUX) { printf("uImage os %d unsupported\n", header.ih_os); return -1; } if (header.ih_arch != arch) { printf("uImage arch %d unsupported\n", header.ih_arch); return -1; } switch (header.ih_comp) { case IH_COMP_NONE: #ifdef HAVE_LIBZ case IH_COMP_GZIP: #endif break; default: printf("uImage uses unsupported compression method\n"); return -1; } if (be32_to_cpu(header.ih_size) > len - sizeof(header)) { printf("uImage header claims that image has %d bytes\n", be32_to_cpu(header.ih_size)); printf("we read only %lld bytes.\n", (long long)len - sizeof(header)); return -1; } #ifdef HAVE_LIBZ crc = crc32(0, (void *)buf + sizeof(header), be32_to_cpu(header.ih_size)); if (crc != be32_to_cpu(header.ih_dcrc)) { printf("uImage: The data CRC does not match. Computed: %08x " "expected %08x\n", crc, be32_to_cpu(header.ih_dcrc)); return -1; } #endif return (int)header.ih_type; } /* * To conform to the 'probe' routine in file_type struct, * we return : * 0 - If the image is valid 'type' image. * * Now, we have to pass on the 'errors' in the image. So, * * -1 - If the image is corrupted. * 1 - If the image is not a uImage. */ int uImage_probe_kernel(const unsigned char *buf, off_t len, unsigned int arch) { int type = uImage_probe(buf, len, arch); if (type < 0) return -1; return !(type == IH_TYPE_KERNEL || type == IH_TYPE_KERNEL_NOLOAD); } int uImage_probe_ramdisk(const unsigned char *buf, off_t len, unsigned int arch) { int type = uImage_probe(buf, len, arch); if (type < 0) return -1; return !(type == IH_TYPE_RAMDISK); } #ifdef HAVE_LIBZ /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ static int uImage_gz_load(const unsigned char *buf, off_t len, struct Image_info *image) { int ret; z_stream strm; unsigned int skip; unsigned int flags; unsigned char *uncomp_buf; unsigned int mem_alloc; mem_alloc = 10 * 1024 * 1024; uncomp_buf = malloc(mem_alloc); if (!uncomp_buf) return -1; memset(&strm, 0, sizeof(strm)); /* Skip magic, method, time, flags, os code ... */ skip = 10; /* check GZ magic */ if (buf[0] != 0x1f || buf[1] != 0x8b) return -1; flags = buf[3]; if (buf[2] != Z_DEFLATED || (flags & RESERVED) != 0) { puts ("Error: Bad gzipped data\n"); return -1; } if (flags & EXTRA_FIELD) { skip += 2; skip += buf[10]; skip += buf[11] << 8; } if (flags & ORIG_NAME) { while (buf[skip++]) ; } if (flags & COMMENT) { while (buf[skip++]) ; } if (flags & HEAD_CRC) skip += 2; strm.avail_in = len - skip; strm.next_in = (void *)buf + skip; /* - activates parsing gz headers */ ret = inflateInit2(&strm, -MAX_WBITS); if (ret != Z_OK) return -1; strm.next_out = uncomp_buf; strm.avail_out = mem_alloc; do { ret = inflate(&strm, Z_FINISH); if (ret == Z_STREAM_END) break; if (ret == Z_OK || ret == Z_BUF_ERROR) { void *new_buf; int inc_buf = 5 * 1024 * 1024; mem_alloc += inc_buf; new_buf = realloc(uncomp_buf, mem_alloc); if (!new_buf) { inflateEnd(&strm); free(uncomp_buf); return -1; } uncomp_buf = new_buf; strm.next_out = uncomp_buf + mem_alloc - inc_buf; strm.avail_out = inc_buf; } else { printf("Error during decompression %d\n", ret); return -1; } } while (1); inflateEnd(&strm); image->buf = uncomp_buf; image->len = mem_alloc - strm.avail_out; return 0; } #else static int uImage_gz_load(const unsigned char *UNUSED(buf), off_t UNUSED(len), struct Image_info *UNUSED(image)) { return -1; } #endif int uImage_load(const unsigned char *buf, off_t len, struct Image_info *image) { const struct image_header *header = (const struct image_header *)buf; const unsigned char *img_buf = buf + sizeof(struct image_header); off_t img_len = be32_to_cpu(header->ih_size); /* * Prevent loading a modified image. * CRC check is perfomed only when zlib is compiled * in. This check will help us to detect * size related vulnerabilities. */ if (img_len != (len - sizeof(struct image_header))) { printf("Image size doesn't match the header\n"); return -1; } image->base = cpu_to_be32(header->ih_load); image->ep = cpu_to_be32(header->ih_ep); switch (header->ih_comp) { case IH_COMP_NONE: image->buf = img_buf; image->len = img_len; return 0; break; case IH_COMP_GZIP: /* * uboot doesn't decompress the RAMDISK images. * Comply to the uboot behaviour. */ if (header->ih_type == IH_TYPE_RAMDISK) { image->buf = img_buf; image->len = img_len; return 0; } else return uImage_gz_load(img_buf, img_len, image); break; default: return -1; } }