Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions llamafile/llamafile.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ static struct llamafile *llamafile_open_zip(const char *prog, const char *fname,
goto Failure;
file->size = rc;

// the true extent of the file on disk. file->size is overwritten below with
// the size the zip central directory *claims*, so keep the real one to
// validate that claim against.
const uint64_t zipsize = (uint64_t)rc;

// read the last 64kb of file
// the zip file format magic can be anywhere in there
int amt;
Expand Down Expand Up @@ -118,15 +123,18 @@ static struct llamafile *llamafile_open_zip(const char *prog, const char *fname,
(long)kZipCdir64HdrMinSize &&
ZIP_READ32(bufdata) == kZipCdir64HdrMagic &&
ZIP_CDIR64_RECORDS(bufdata) == ZIP_CDIR64_RECORDSONDISK(bufdata) &&
ZIP_CDIR64_RECORDS(bufdata) && ZIP_CDIR64_SIZE(bufdata) <= INT_MAX) {
ZIP_CDIR64_RECORDS(bufdata) &&
ZIP_CDIR64_SIZE(bufdata) >= kZipCfileHdrMinSize &&
ZIP_CDIR64_SIZE(bufdata) <= INT_MAX) {
cnt = ZIP_CDIR64_RECORDS(bufdata);
off = ZIP_CDIR64_OFFSET(bufdata);
amt = ZIP_CDIR64_SIZE(bufdata);
break;
}
if (magic == kZipCdirHdrMagic && i + kZipCdirHdrMinSize <= amt &&
ZIP_CDIR_RECORDS(bufdata + i) == ZIP_CDIR_RECORDSONDISK(bufdata + i) &&
ZIP_CDIR_RECORDS(bufdata + i) && ZIP_CDIR_SIZE(bufdata + i) <= INT_MAX &&
ZIP_CDIR_RECORDS(bufdata + i) && ZIP_CDIR_SIZE(bufdata + i) >= kZipCfileHdrMinSize &&
ZIP_CDIR_SIZE(bufdata + i) <= INT_MAX &&
ZIP_CDIR_OFFSET(bufdata + i) != 0xffffffffu) {
cnt = ZIP_CDIR_RECORDS(bufdata + i);
off = ZIP_CDIR_OFFSET(bufdata + i);
Expand Down Expand Up @@ -174,8 +182,25 @@ static struct llamafile *llamafile_open_zip(const char *prog, const char *fname,
: (entry_name_len > 5 &&
!memcasecmp(entry_name_bytes + entry_name_len - 5, ".gguf", 5)))) {
zip_name = gc(strndup(entry_name_bytes, entry_name_len));
off = get_zip_cfile_offset(cdirdata + entry_offset);
file->size = get_zip_cfile_compressed_size(cdirdata + entry_offset);
// both accessors return int64_t and use -1 to signal failure, e.g. a
// zip64 sentinel size with no zip64 extra field to satisfy it.
// Assigning that straight into uint64_t/size_t yields UINT64_MAX and
// makes the mapsize arithmetic below wrap.
int64_t entry_off = get_zip_cfile_offset(cdirdata + entry_offset);
int64_t entry_size = get_zip_cfile_compressed_size(cdirdata + entry_offset);
if (entry_off < 0 || entry_size < 0) {
fprintf(stderr, "%s: warning: zip entry has an unreadable offset or size\n", prog);
goto Invalid;
}
// the declared window must lie inside the file. written as a
// subtraction so it cannot overflow.
if ((uint64_t)entry_size > zipsize ||
(uint64_t)entry_off > zipsize - (uint64_t)entry_size) {
fprintf(stderr, "%s: warning: zip entry extends past the end of the file\n", prog);
goto Invalid;
}
off = (uint64_t)entry_off;
file->size = (size_t)entry_size;
cdir_offset = entry_offset;
++found;
}
Expand Down Expand Up @@ -214,6 +239,15 @@ static struct llamafile *llamafile_open_zip(const char *prog, const char *fname,
}
off += ZIP_LFILE_HDRSIZE(lfile);

// the local header's name and extra fields are attacker controlled (two
// uint16s, up to 131100 bytes total), so the window has to be revalidated
// after skipping them. written as a subtraction so it cannot overflow.
if (off > zipsize || (uint64_t)file->size > zipsize - off) {
fprintf(stderr, "%s: warning: zip entry data extends past the end of the file\n",
file->fname);
goto Invalid;
}

// perform sanity check
// mapping weights for apple metal gpu requires 16kb alignment
if (off & 16383)
Expand Down