Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1Description: patch for CVE-2020-14315 2 A memory corruption vulnerability is present in bspatch as shipped in 3 Colin Percival’s bsdiff tools version 4.3. Insufficient checks when 4 handling external inputs allows an attacker to bypass the sanity checks 5 in place and write out of a dynamically allocated buffer boundaries. 6Source: https://svnweb.freebsd.org/base/head/usr.bin/bsdiff/bspatch/bspatch.c?revision=352742&view=co 7Author: tony mancill <tmancill@debian.org> 8Comment: The patch was created by comparing the Debian sources to the 9 "Confirmed Patched Version" [1] documented in the 10 X41 D-SEC GmbH Security Advisory: X41-2020-006 [2]. 11 References to FreeBSD capsicum have been dropped. Definitions for 12 TYPE_MINIMUM and TYPE_MAXIMUM have been borrowed from the Debian 13 coreutils package sources but originate in gnulib [3] and are used to 14 define OFF_MIN and OFF_MAX (limits of off_t). Whitespace changes from 15 the confirmed patched version are also included and keep the difference 16 between the Debian sources and the confirmed patched version minimal. 17 . 18 [1] https://svnweb.freebsd.org/base/head/usr.bin/bsdiff/bspatch/bspatch.c?revision=352742&view=co 19 [2] https://www.openwall.com/lists/oss-security/2020/07/09/2 20 [3] https://www.gnu.org/software/gnulib/ 21Last-Update: 2021-04-03 22Forwarded: not-needed 23Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=964796 24 25--- a/bspatch.c 26+++ b/bspatch.c 27@@ -1,4 +1,6 @@ 28 /*- 29+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 30+ * 31 * Copyright 2003-2005 Colin Percival 32 * All rights reserved 33 * 34@@ -24,56 +26,148 @@ 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38+#include <sys/cdefs.h> 39 #if 0 40-__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $"); 41+__FBSDID("$FreeBSD$"); 42 #endif 43 44 #include <bzlib.h> 45-#include <stdlib.h> 46+#include <err.h> 47+#include <fcntl.h> 48+#include <libgen.h> 49+#include <limits.h> 50+#include <stdint.h> 51 #include <stdio.h> 52+#include <stdlib.h> 53 #include <string.h> 54-#include <err.h> 55 #include <unistd.h> 56-#include <fcntl.h> 57+ 58+#ifndef O_BINARY 59+#define O_BINARY 0 60+#endif 61+#define HEADER_SIZE 32 62+ 63+/* TYPE_MINIMUM and TYPE_MAXIMUM taken from coreutils */ 64+#ifndef TYPE_MINIMUM 65+#define TYPE_MINIMUM(t) \ 66+ ((t) ((t) 0 < (t) -1 ? (t) 0 : ~ TYPE_MAXIMUM (t))) 67+#endif 68+#ifndef TYPE_MAXIMUM 69+#define TYPE_MAXIMUM(t) \ 70+ ((t) ((t) 0 < (t) -1 \ 71+ ? (t) -1 \ 72+ : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1))) 73+#endif 74+ 75+#ifndef OFF_MAX 76+#define OFF_MAX TYPE_MAXIMUM(off_t) 77+#endif 78+ 79+#ifndef OFF_MIN 80+#define OFF_MIN TYPE_MINIMUM(off_t) 81+#endif 82+ 83+static char *newfile; 84+static int dirfd = -1; 85+ 86+static void 87+exit_cleanup(void) 88+{ 89+ 90+ if (dirfd != -1 && newfile != NULL) 91+ if (unlinkat(dirfd, newfile, 0)) 92+ warn("unlinkat"); 93+} 94+ 95+static inline off_t 96+add_off_t(off_t a, off_t b) 97+{ 98+ off_t result; 99+ 100+#if __GNUC__ >= 5 101+ if (__builtin_add_overflow(a, b, &result)) 102+ errx(1, "Corrupt patch"); 103+#else 104+ if ((b > 0 && a > OFF_MAX - b) || (b < 0 && a < OFF_MIN - b)) 105+ errx(1, "Corrupt patch"); 106+ result = a + b; 107+#endif 108+ return result; 109+} 110 111 static off_t offtin(u_char *buf) 112 { 113 off_t y; 114 115- y=buf[7]&0x7F; 116- y=y*256;y+=buf[6]; 117- y=y*256;y+=buf[5]; 118- y=y*256;y+=buf[4]; 119- y=y*256;y+=buf[3]; 120- y=y*256;y+=buf[2]; 121- y=y*256;y+=buf[1]; 122- y=y*256;y+=buf[0]; 123+ y = buf[7] & 0x7F; 124+ y = y * 256; y += buf[6]; 125+ y = y * 256; y += buf[5]; 126+ y = y * 256; y += buf[4]; 127+ y = y * 256; y += buf[3]; 128+ y = y * 256; y += buf[2]; 129+ y = y * 256; y += buf[1]; 130+ y = y * 256; y += buf[0]; 131 132- if(buf[7]&0x80) y=-y; 133+ if (buf[7] & 0x80) 134+ y = -y; 135 136- return y; 137+ return (y); 138 } 139 140-int main(int argc,char * argv[]) 141+static void 142+usage(void) 143 { 144- FILE * f, * cpf, * dpf, * epf; 145- BZFILE * cpfbz2, * dpfbz2, * epfbz2; 146+ 147+ fprintf(stderr, "usage: bspatch oldfile newfile patchfile\n"); 148+ exit(1); 149+} 150+ 151+int main(int argc, char *argv[]) 152+{ 153+ FILE *f, *cpf, *dpf, *epf; 154+ BZFILE *cpfbz2, *dpfbz2, *epfbz2; 155+ char *directory, *namebuf; 156 int cbz2err, dbz2err, ebz2err; 157- int fd; 158- ssize_t oldsize,newsize; 159- ssize_t bzctrllen,bzdatalen; 160- u_char header[32],buf[8]; 161+ int newfd, oldfd; 162+ off_t oldsize, newsize; 163+ off_t bzctrllen, bzdatalen; 164+ u_char header[HEADER_SIZE], buf[8]; 165 u_char *old, *new; 166- off_t oldpos,newpos; 167+ off_t oldpos, newpos; 168 off_t ctrl[3]; 169- off_t lenread; 170- off_t i; 171+ off_t i, lenread, offset; 172 173- if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]); 174+ if (argc != 4) 175+ usage(); 176 177 /* Open patch file */ 178- if ((f = fopen(argv[3], "r")) == NULL) 179+ if ((f = fopen(argv[3], "rb")) == NULL) 180+ err(1, "fopen(%s)", argv[3]); 181+ /* Open patch file for control block */ 182+ if ((cpf = fopen(argv[3], "rb")) == NULL) 183+ err(1, "fopen(%s)", argv[3]); 184+ /* open patch file for diff block */ 185+ if ((dpf = fopen(argv[3], "rb")) == NULL) 186 err(1, "fopen(%s)", argv[3]); 187+ /* open patch file for extra block */ 188+ if ((epf = fopen(argv[3], "rb")) == NULL) 189+ err(1, "fopen(%s)", argv[3]); 190+ /* open oldfile */ 191+ if ((oldfd = open(argv[1], O_RDONLY | O_BINARY, 0)) < 0) 192+ err(1, "open(%s)", argv[1]); 193+ /* open directory where we'll write newfile */ 194+ if ((namebuf = strdup(argv[2])) == NULL || 195+ (directory = dirname(namebuf)) == NULL || 196+ (dirfd = open(directory, O_DIRECTORY)) < 0) 197+ err(1, "open %s", argv[2]); 198+ free(namebuf); 199+ if ((newfile = basename(argv[2])) == NULL) 200+ err(1, "basename"); 201+ /* open newfile */ 202+ if ((newfd = openat(dirfd, newfile, 203+ O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0) 204+ err(1, "open(%s)", argv[2]); 205+ atexit(exit_cleanup); 206 207 /* 208 File format: 209@@ -90,104 +185,104 @@ 210 */ 211 212 /* Read header */ 213- if (fread(header, 1, 32, f) < 32) { 214+ if (fread(header, 1, HEADER_SIZE, f) < HEADER_SIZE) { 215 if (feof(f)) 216- errx(1, "Corrupt patch\n"); 217+ errx(1, "Corrupt patch"); 218 err(1, "fread(%s)", argv[3]); 219 } 220 221 /* Check for appropriate magic */ 222 if (memcmp(header, "BSDIFF40", 8) != 0) 223- errx(1, "Corrupt patch\n"); 224+ errx(1, "Corrupt patch"); 225 226 /* Read lengths from header */ 227- bzctrllen=offtin(header+8); 228- bzdatalen=offtin(header+16); 229- newsize=offtin(header+24); 230- if((bzctrllen<0) || (bzdatalen<0) || (newsize<0)) 231- errx(1,"Corrupt patch\n"); 232+ bzctrllen = offtin(header + 8); 233+ bzdatalen = offtin(header + 16); 234+ newsize = offtin(header + 24); 235+ if (bzctrllen < 0 || bzctrllen > OFF_MAX - HEADER_SIZE || 236+ bzdatalen < 0 || bzctrllen + HEADER_SIZE > OFF_MAX - bzdatalen || 237+ newsize < 0 || newsize > SSIZE_MAX) 238+ errx(1, "Corrupt patch"); 239 240 /* Close patch file and re-open it via libbzip2 at the right places */ 241 if (fclose(f)) 242 err(1, "fclose(%s)", argv[3]); 243- if ((cpf = fopen(argv[3], "r")) == NULL) 244- err(1, "fopen(%s)", argv[3]); 245- if (fseeko(cpf, 32, SEEK_SET)) 246- err(1, "fseeko(%s, %lld)", argv[3], 247- (long long)32); 248+ offset = HEADER_SIZE; 249+ if (fseeko(cpf, offset, SEEK_SET)) 250+ err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset); 251 if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL) 252 errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err); 253- if ((dpf = fopen(argv[3], "r")) == NULL) 254- err(1, "fopen(%s)", argv[3]); 255- if (fseeko(dpf, 32 + bzctrllen, SEEK_SET)) 256- err(1, "fseeko(%s, %lld)", argv[3], 257- (long long)(32 + bzctrllen)); 258+ offset = add_off_t(offset, bzctrllen); 259+ if (fseeko(dpf, offset, SEEK_SET)) 260+ err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset); 261 if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL) 262 errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err); 263- if ((epf = fopen(argv[3], "r")) == NULL) 264- err(1, "fopen(%s)", argv[3]); 265- if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET)) 266- err(1, "fseeko(%s, %lld)", argv[3], 267- (long long)(32 + bzctrllen + bzdatalen)); 268+ offset = add_off_t(offset, bzdatalen); 269+ if (fseeko(epf, offset, SEEK_SET)) 270+ err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset); 271 if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL) 272 errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err); 273 274- if(((fd=open(argv[1],O_RDONLY,0))<0) || 275- ((oldsize=lseek(fd,0,SEEK_END))==-1) || 276- ((old=malloc(oldsize+1))==NULL) || 277- (lseek(fd,0,SEEK_SET)!=0) || 278- (read(fd,old,oldsize)!=oldsize) || 279- (close(fd)==-1)) err(1,"%s",argv[1]); 280- if((new=malloc(newsize+1))==NULL) err(1,NULL); 281- 282- oldpos=0;newpos=0; 283- while(newpos<newsize) { 284+ if ((oldsize = lseek(oldfd, 0, SEEK_END)) == -1 || 285+ oldsize > SSIZE_MAX || 286+ (old = malloc(oldsize)) == NULL || 287+ lseek(oldfd, 0, SEEK_SET) != 0 || 288+ read(oldfd, old, oldsize) != oldsize || 289+ close(oldfd) == -1) 290+ err(1, "%s", argv[1]); 291+ if ((new = malloc(newsize)) == NULL) 292+ err(1, NULL); 293+ 294+ oldpos = 0; 295+ newpos = 0; 296+ while (newpos < newsize) { 297 /* Read control data */ 298- for(i=0;i<=2;i++) { 299+ for (i = 0; i <= 2; i++) { 300 lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8); 301 if ((lenread < 8) || ((cbz2err != BZ_OK) && 302 (cbz2err != BZ_STREAM_END))) 303- errx(1, "Corrupt patch\n"); 304- ctrl[i]=offtin(buf); 305- }; 306+ errx(1, "Corrupt patch"); 307+ ctrl[i] = offtin(buf); 308+ } 309 310 /* Sanity-check */ 311- if ((ctrl[0] < 0) || (ctrl[1] < 0)) 312- errx(1,"Corrupt patch\n"); 313+ if (ctrl[0] < 0 || ctrl[0] > INT_MAX || 314+ ctrl[1] < 0 || ctrl[1] > INT_MAX) 315+ errx(1, "Corrupt patch"); 316 317 /* Sanity-check */ 318- if(newpos+ctrl[0]>newsize) 319- errx(1,"Corrupt patch\n"); 320+ if (add_off_t(newpos, ctrl[0]) > newsize) 321+ errx(1, "Corrupt patch"); 322 323 /* Read diff string */ 324 lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]); 325 if ((lenread < ctrl[0]) || 326 ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END))) 327- errx(1, "Corrupt patch\n"); 328+ errx(1, "Corrupt patch"); 329 330 /* Add old data to diff string */ 331- for(i=0;i<ctrl[0];i++) 332- if((oldpos+i>=0) && (oldpos+i<oldsize)) 333- new[newpos+i]+=old[oldpos+i]; 334+ for (i = 0; i < ctrl[0]; i++) 335+ if (add_off_t(oldpos, i) < oldsize) 336+ new[newpos + i] += old[oldpos + i]; 337 338 /* Adjust pointers */ 339- newpos+=ctrl[0]; 340- oldpos+=ctrl[0]; 341+ newpos = add_off_t(newpos, ctrl[0]); 342+ oldpos = add_off_t(oldpos, ctrl[0]); 343 344 /* Sanity-check */ 345- if(newpos+ctrl[1]>newsize) 346- errx(1,"Corrupt patch\n"); 347+ if (add_off_t(newpos, ctrl[1]) > newsize) 348+ errx(1, "Corrupt patch"); 349 350 /* Read extra string */ 351 lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]); 352 if ((lenread < ctrl[1]) || 353 ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END))) 354- errx(1, "Corrupt patch\n"); 355+ errx(1, "Corrupt patch"); 356 357 /* Adjust pointers */ 358- newpos+=ctrl[1]; 359- oldpos+=ctrl[2]; 360- }; 361+ newpos = add_off_t(newpos, ctrl[1]); 362+ oldpos = add_off_t(oldpos, ctrl[2]); 363+ } 364 365 /* Clean up the bzip2 reads */ 366 BZ2_bzReadClose(&cbz2err, cpfbz2); 367@@ -197,12 +292,13 @@ 368 err(1, "fclose(%s)", argv[3]); 369 370 /* Write the new file */ 371- if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) || 372- (write(fd,new,newsize)!=newsize) || (close(fd)==-1)) 373- err(1,"%s",argv[2]); 374+ if (write(newfd, new, newsize) != newsize || close(newfd) == -1) 375+ err(1, "%s", argv[2]); 376+ /* Disable atexit cleanup */ 377+ newfile = NULL; 378 379 free(new); 380 free(old); 381 382- return 0; 383+ return (0); 384 }