commit 9382c10a8a595c1f3341daf047ada04346b51408 from: Stefan Sperling via: Thomas Adam date: Thu Feb 23 16:33:51 2023 UTC fix tog diff between arbitrary commits Don't assume commit info is always written. This is only true when diffing a commit against (one of) its direct parent(s). Otherwise we perform an invalid read on a tmp got_diff_line array and end up passing bogus offsets to fseeko(). Bug found and fixed by stsp with a minor tweak by me. ok jamsek for stsp's initial diff ok stsp@ commit - ec629cf4ad5084d3f423ea1b906903baa050918e commit + 9382c10a8a595c1f3341daf047ada04346b51408 blob - c208567d258f17c2d797dd89d98c42d49fdb4d3b blob + 538114b0dd798c030f1eb44257fbac707152a44f --- tog/tog.c +++ tog/tog.c @@ -4583,14 +4583,23 @@ cat_diff(FILE *dst, FILE *src, struct got_diff_line ** return got_ferror(dst, GOT_ERR_IO); } + if (s_nlines == 0 && *d_nlines == 0) + return NULL; + /* - * The diff driver initialises the first line at offset zero when the - * array isn't prepopulated, skip it; we already have it in *d_lines. + * If commit info was in dst, increment line offsets + * of the appended diff content, but skip s_lines[0] + * because offset zero is already in *d_lines. */ - for (i = 1; i < s_nlines; ++i) - s_lines[i].offset += (*d_lines)[*d_nlines - 1].offset; + if (*d_nlines > 0) { + for (i = 1; i < s_nlines; ++i) + s_lines[i].offset += (*d_lines)[*d_nlines - 1].offset; - --s_nlines; + if (s_nlines > 0) { + --s_nlines; + ++s_lines; + } + } p = reallocarray(*d_lines, *d_nlines + s_nlines, sizeof(*p)); if (p == NULL) { @@ -4600,7 +4609,7 @@ cat_diff(FILE *dst, FILE *src, struct got_diff_line ** *d_lines = p; - memcpy(*d_lines + *d_nlines, s_lines + 1, s_nlines * sizeof(*s_lines)); + memcpy(*d_lines + *d_nlines, s_lines, s_nlines * sizeof(*s_lines)); *d_nlines += s_nlines; return NULL;