lol

Merge pull request #145955 from alyaeanyx/bsdiff

bsdiff: security and bug fixes

authored by

Thiago Kenji Okada and committed by
GitHub
60b8a7ea 28e1b1a5

+406 -2
+384
pkgs/tools/compression/bsdiff/CVE-2020-14315.patch
··· 1 + Description: 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. 6 + Source: https://svnweb.freebsd.org/base/head/usr.bin/bsdiff/bspatch/bspatch.c?revision=352742&view=co 7 + Author: tony mancill <tmancill@debian.org> 8 + Comment: 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/ 21 + Last-Update: 2021-04-03 22 + Forwarded: not-needed 23 + Bug-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 + }
+22 -2
pkgs/tools/compression/bsdiff/default.nix
··· 1 - { lib, stdenv, fetchurl, bzip2 }: 1 + { lib, stdenv, fetchurl, fetchpatch, bzip2 }: 2 2 3 3 stdenv.mkDerivation rec { 4 4 pname = "bsdiff"; ··· 10 10 }; 11 11 12 12 buildInputs = [ bzip2 ]; 13 - patches = [ ./include-systypes.patch ]; 13 + patches = [ 14 + (fetchpatch { 15 + url = "https://sources.debian.org/data/main/b/bsdiff/4.3-22/debian/patches/20-CVE-2014-9862.patch"; 16 + sha256 = "sha256-3UuUfNvShQ8fLqxCKUTb/n4BmjL4+Nl7aEqCxYrrERQ="; 17 + }) 18 + ./CVE-2020-14315.patch 19 + ./include-systypes.patch 20 + ] ++ lib.optional stdenv.hostPlatform.isLinux [ 21 + (fetchpatch { 22 + url = "https://sources.debian.org/data/main/b/bsdiff/4.3-22/debian/patches/30-bug-632585-mmap-src-file-instead-of-malloc-read-it.patch"; 23 + sha256 = "sha256-esbhz2/efUiuQDuF7LGfSeEn3/f1WbqCxQpTs2A0ulI="; 24 + }) 25 + (fetchpatch { 26 + url = "https://sources.debian.org/data/main/b/bsdiff/4.3-22/debian/patches/31-bug-632585-mmap-dst-file-instead-of-malloc-read-it.patch"; 27 + sha256 = "sha256-Of4aOcI0rsgdRzPqyw2VRn2p9wQuo3hdlgDTBdXGzoc="; 28 + }) 29 + (fetchpatch { 30 + url = "https://sources.debian.org/data/main/b/bsdiff/4.3-22/debian/patches/32-bug-632585-use-int32_t-instead-off_t-for-file-size.patch"; 31 + sha256 = "sha256-SooFnFK4uKNXvXQb/LEcH8GocnRtkryExI4b3BZTsAY="; 32 + }) 33 + ]; 14 34 15 35 buildPhase = '' 16 36 $CC -O3 -lbz2 bspatch.c -o bspatch