commit 34fca9c35c1a35f6e163edd53f5cf674b30c1799 from: Stefan Sperling date: Sun Nov 11 10:10:11 2018 UTC bounds checks before memcpy in got_delta_apply_in_mem() commit - 246e1c78b7c0d1b05a03e5801ffd25d90014f4a4 commit + 34fca9c35c1a35f6e163edd53f5cf674b30c1799 blob - 1893ac395efc26967299e737c552d73fe606efc2 blob + 9959137774da2dd00f419381147d0952d0d20785 --- lib/delta.c +++ lib/delta.c @@ -258,8 +258,9 @@ got_delta_get_sizes(uint64_t *base_size, uint64_t *res } const struct got_error * -got_delta_apply_in_mem(uint8_t *base_buf, const uint8_t *delta_buf, - size_t delta_len, uint8_t *outbuf, size_t *outsize) +got_delta_apply_in_mem(uint8_t *base_buf, size_t base_bufsz, + const uint8_t *delta_buf, size_t delta_len, uint8_t *outbuf, + size_t *outsize, size_t maxoutsize) { const struct got_error *err = NULL; uint64_t base_size, result_size; @@ -286,6 +287,9 @@ got_delta_apply_in_mem(uint8_t *base_buf, const uint8_ err = parse_opcode(&offset, &len, &p, &remain); if (err) break; + if (base_bufsz < offset + len || + *outsize + len > maxoutsize) + return got_error(GOT_ERR_BAD_DELTA); memcpy(outbuf + *outsize, base_buf + offset, len); if (err == NULL) { *outsize += len; @@ -303,7 +307,7 @@ got_delta_apply_in_mem(uint8_t *base_buf, const uint8_ err = next_delta_byte(&p, &remain); if (err) break; - if (remain < len) + if (remain < len || *outsize + len > maxoutsize) return got_error(GOT_ERR_BAD_DELTA); memcpy(outbuf + *outsize, p, len); p += len; blob - 6f61627e302246e2daad1fa4ffdf095067b082ca blob + a2cb4a3ab0d26620028ba99100ba2d73563520c9 --- lib/got_lib_delta.h +++ lib/got_lib_delta.h @@ -39,8 +39,8 @@ const struct got_error *got_delta_chain_get_base_type( struct got_delta_chain *); const struct got_error *got_delta_get_sizes(uint64_t *, uint64_t *, const uint8_t *, size_t); -const struct got_error *got_delta_apply_in_mem(uint8_t *, const uint8_t *, - size_t, uint8_t *, size_t *); +const struct got_error *got_delta_apply_in_mem(uint8_t *, size_t, + const uint8_t *, size_t, uint8_t *, size_t *, size_t); const struct got_error *got_delta_apply(FILE *, const uint8_t *, size_t, FILE *, size_t *); blob - fd18041fae8a7d318c0d9a6a115419cf8cd82e18 blob + b16b4be257ce68d10cdae28713c204ef2a8862dc --- lib/pack.c +++ lib/pack.c @@ -994,7 +994,7 @@ dump_delta_chain_to_file(size_t *result_size, struct g const struct got_error *err = NULL; struct got_delta *delta; uint8_t *base_buf = NULL, *accum_buf = NULL; - size_t accum_size = 0; + size_t base_bufsz = 0, accum_size = 0; uint64_t max_size; int n = 0; @@ -1018,7 +1018,7 @@ dump_delta_chain_to_file(size_t *result_size, struct g /* Deltas are ordered in ascending order. */ SIMPLEQ_FOREACH(delta, &deltas->entries, entry) { if (n == 0) { - size_t base_len, mapoff; + size_t mapoff; off_t delta_data_offset; /* Plain object types are the delta base. */ @@ -1046,29 +1046,20 @@ dump_delta_chain_to_file(size_t *result_size, struct g if (pack->map) { mapoff = (size_t)delta_data_offset; err = got_inflate_to_file_mmap( - &base_len, pack->map, mapoff, + &base_bufsz, pack->map, mapoff, pack->filesize - mapoff, base_file); } else - err = got_inflate_to_file_fd(&base_len, - pack->fd, base_file); + err = got_inflate_to_file_fd( + &base_bufsz, pack->fd, base_file); } else { if (pack->map) { mapoff = (size_t)delta_data_offset; err = got_inflate_to_mem_mmap(&base_buf, - &base_len, pack->map, mapoff, + &base_bufsz, pack->map, mapoff, pack->filesize - mapoff); } else err = got_inflate_to_mem_fd(&base_buf, - &base_len, pack->fd); - if (base_len < max_size) { - uint8_t *p; - p = reallocarray(base_buf, 1, max_size); - if (p == NULL) { - err = got_error_from_errno(); - goto done; - } - base_buf = p; - } + &base_bufsz, pack->fd); } if (err) goto done; @@ -1079,8 +1070,9 @@ dump_delta_chain_to_file(size_t *result_size, struct g } if (base_buf) { - err = got_delta_apply_in_mem(base_buf, delta->delta_buf, - delta->delta_len, accum_buf, &accum_size); + err = got_delta_apply_in_mem(base_buf, base_bufsz, + delta->delta_buf, delta->delta_len, accum_buf, + &accum_size, max_size); n++; } else { err = got_delta_apply(base_file, delta->delta_buf, @@ -1096,6 +1088,22 @@ dump_delta_chain_to_file(size_t *result_size, struct g /* Accumulated delta becomes the new base. */ if (base_buf) { uint8_t *tmp = accum_buf; + /* + * Base buffer switches roles with accumulation + * buffer. Ensure it can hold the largest + * result in the delta chain. The initial + * allocation might have been smaller. + */ + if (base_bufsz < max_size) { + uint8_t *p; + p = reallocarray(base_buf, 1, max_size); + if (p == NULL) { + err = got_error_from_errno(); + goto done; + } + base_buf = p; + base_bufsz = max_size; + } accum_buf = base_buf; base_buf = tmp; } else { @@ -1133,7 +1141,7 @@ dump_delta_chain_to_mem(uint8_t **outbuf, size_t *outl const struct got_error *err = NULL; struct got_delta *delta; uint8_t *base_buf = NULL, *accum_buf = NULL; - size_t accum_size; + size_t base_bufsz = 0, accum_size = 0; uint64_t max_size; int n = 0; @@ -1153,7 +1161,6 @@ dump_delta_chain_to_mem(uint8_t **outbuf, size_t *outl /* Deltas are ordered in ascending order. */ SIMPLEQ_FOREACH(delta, &deltas->entries, entry) { if (n == 0) { - size_t base_len; size_t delta_data_offset; /* Plain object types are the delta base. */ @@ -1173,7 +1180,7 @@ dump_delta_chain_to_mem(uint8_t **outbuf, size_t *outl if (pack->map) { size_t mapoff = (size_t)delta_data_offset; err = got_inflate_to_mem_mmap(&base_buf, - &base_len, pack->map, mapoff, + &base_bufsz, pack->map, mapoff, pack->filesize - mapoff); } else { if (lseek(pack->fd, delta_data_offset, SEEK_SET) @@ -1182,25 +1189,17 @@ dump_delta_chain_to_mem(uint8_t **outbuf, size_t *outl goto done; } err = got_inflate_to_mem_fd(&base_buf, - &base_len, pack->fd); + &base_bufsz, pack->fd); } if (err) goto done; - if (base_len < max_size) { - uint8_t *p; - p = reallocarray(base_buf, 1, max_size); - if (p == NULL) { - err = got_error_from_errno(); - goto done; - } - base_buf = p; - } n++; continue; } - err = got_delta_apply_in_mem(base_buf, delta->delta_buf, - delta->delta_len, accum_buf, &accum_size); + err = got_delta_apply_in_mem(base_buf, base_bufsz, + delta->delta_buf, delta->delta_len, accum_buf, + &accum_size, max_size); n++; if (err) goto done; @@ -1208,6 +1207,21 @@ dump_delta_chain_to_mem(uint8_t **outbuf, size_t *outl if (n < deltas->nentries) { /* Accumulated delta becomes the new base. */ uint8_t *tmp = accum_buf; + /* + * Base buffer switches roles with accumulation buffer. + * Ensure it can hold the largest result in the delta + * chain. Initial allocation might have been smaller. + */ + if (base_bufsz < max_size) { + uint8_t *p; + p = reallocarray(base_buf, 1, max_size); + if (p == NULL) { + err = got_error_from_errno(); + goto done; + } + base_buf = p; + base_bufsz = max_size; + } accum_buf = base_buf; base_buf = tmp; }