On Wed, 2018-11-28 at 08:20 +0700, Robert Elz wrote: > Date: Wed, 28 Nov 2018 00:23:47 +0100 > From: =?UTF-8?Q?Micha=C5=82_G=C3=B3rny?= <mgorny%gentoo.org@localhost> > Message-ID: <1543361027.17222.11.camel%gentoo.org@localhost> > > I have no idea whether this is something that we want or not, > but assuming that we do ... > > | so I'd appreciate some pointers if I'm doing things right. > > I think you need to watch for sign extension from *str (str is a char *, not > u_char *) which probably means &0xFF is needed, and I'd suggest > that rather than doing <<8 >> 8 and comparing for equality, testing > whether you're going to overflow when doing a <<8 is better done as > if (tval > (UINTMAX_MAX/256)) > > I also see that the other conversions work if len == 0 (they return 0), > which your one doesn't, it might be worth a "len > 0" in the initial test > (so you never look at *str if len == 0). Thank you for your comments. FTR, I don't think those functions can even take len==0 (given they're passed only positive values by design) but I suppose there's no harm in checking for it explicitly. I've also changed the test case to check whether *str >= 0x80 works correctly. Here's the updated patch: Index: bin/pax/gen_subs.c =================================================================== RCS file: /pub/NetBSD-CVS/src/bin/pax/gen_subs.c,v retrieving revision 1.36 diff -u -B -d -u -p -r1.36 gen_subs.c --- bin/pax/gen_subs.c 9 Aug 2012 08:09:21 -0000 1.36 +++ bin/pax/gen_subs.c 28 Nov 2018 17:23:45 -0000 @@ -306,12 +306,10 @@ u32_asc(uintmax_t val, char *str, int le /* * asc_umax() - * convert hex/octal character string into a uintmax. We do - * not have to to check for overflow! (the headers in all supported - * formats are not large enough to create an overflow). + * convert hex/octal/base-256 value into a uintmax. * NOTE: strings passed to us are NOT TERMINATED. * Return: - * uintmax_t value + * uintmax_t value; UINTMAX_MAX for overflow/negative */ uintmax_t @@ -323,6 +321,30 @@ asc_umax(char *str, int len, int base) stop = str + len; /* + * if the highest bit of first byte is set, it's base-256 encoded + * (base-256 is basically (n-1)-bit big endian signed + */ + if (str < stop && (*str & 0x80)) { + /* + * uintmax_t can't be negative, so fail on negative numbers + */ + if (*str & 0x40) + return UINTMAX_MAX; + + tval = *str++ & 0x3f; + while (str < stop) { + /* + * check for overflow + */ + if (tval > (UINTMAX_MAX/256)) + return UINTMAX_MAX; + tval = (tval << 8) | ((*str++) & 0xFF); + } + + return tval; + } + + /* * skip over leading blanks and zeros */ while ((str < stop) && ((*str == ' ') || (*str == '0'))) Index: bin/pax/tar.c =================================================================== RCS file: /pub/NetBSD-CVS/src/bin/pax/tar.c,v retrieving revision 1.73 diff -u -B -d -u -p -r1.73 tar.c --- bin/pax/tar.c 19 Dec 2015 18:28:54 -0000 1.73 +++ bin/pax/tar.c 28 Nov 2018 17:23:45 -0000 @@ -486,6 +486,8 @@ tar_rd(ARCHD *arcn, char *buf) arcn->sb.st_uid = (uid_t)asc_u32(hd->uid, sizeof(hd->uid), OCT); arcn->sb.st_gid = (gid_t)asc_u32(hd->gid, sizeof(hd->gid), OCT); arcn->sb.st_size = (off_t)ASC_OFFT(hd->size, sizeof(hd->size), OCT); + if (arcn->sb.st_size == -1) + return -1; arcn->sb.st_mtime = (time_t)(int32_t)asc_u32(hd->mtime, sizeof(hd->mtime), OCT); arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; @@ -860,6 +862,8 @@ ustar_rd(ARCHD *arcn, char *buf) arcn->sb.st_mode = (mode_t)(asc_u32(hd->mode, sizeof(hd->mode), OCT) & 0xfff); arcn->sb.st_size = (off_t)ASC_OFFT(hd->size, sizeof(hd->size), OCT); + if (arcn->sb.st_size == -1) + return -1; arcn->sb.st_mtime = (time_t)(int32_t)asc_u32(hd->mtime, sizeof(hd->mtime), OCT); arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; Index: tests/bin/tar/t_tar.sh =================================================================== RCS file: /pub/NetBSD-CVS/src/tests/bin/tar/t_tar.sh,v retrieving revision 1.1 diff -u -B -d -u -p -r1.1 t_tar.sh --- tests/bin/tar/t_tar.sh 17 Mar 2012 16:33:11 -0000 1.1 +++ tests/bin/tar/t_tar.sh 28 Nov 2018 17:23:45 -0000 @@ -45,7 +45,53 @@ append_body() { atf_check -s eq:0 -o empty -e empty cmp file1.tar file2.tar } +atf_test_case rd_base256_size +rd_base256_size_head() { + atf_set "descr" "Test extracting an archive whose member size" \ + "is encoded as base-256 number (GNU style)" +} +rd_base256_size_body() { + # prepare random file data for comparison + # take 0x1200CF bytes in order to test that we: + # - handle multiple bytes of size field correctly + # - do not fail on NUL bytes + # - do not fail on char values > 0x80 (with signed char) + dd if=/dev/urandom of=reference.bin bs=1179855 count=1 + # write test archive header + # - filename + printf 'output.bin' > test.tar + # - pad to 100 octets + head -c 90 /dev/zero >> test.tar + # - mode, uid, gid + printf '%07d\0%07d\0%07d\0' 644 177776 177775 >> test.tar + # - size (base-256) + printf '\x80\0\0\0\0\0\0\0\0\x12\x00\xCF' >> test.tar + # - timestamp, checksum + printf '%011d\0%06d\0 0' 13377546642 12460 >> test.tar + # - pad empty linkname (100 octets) + head -c 100 /dev/zero >> test.tar + # - magic, user name + printf 'ustar \0nobody' >> test.tar + # - pad user name field to 32 bytes + head -c 26 /dev/zero >> test.tar + # - group name + printf 'nogroup' >> test.tar + # - pad to full block + head -c 208 /dev/zero >> test.tar + # append file data to the test archive + cat reference.bin >> test.tar + # pad to full block + append two terminating null blocks + head -c 1450 /dev/zero >> test.tar + + # test extracting the test archive + atf_check -s eq:0 -o empty -e empty tar -xf test.tar + + # ensure that output.bin is equal to reference.bin + atf_check -s eq:0 -o empty -e empty cmp output.bin reference.bin +} + atf_init_test_cases() { atf_add_test_case append + atf_add_test_case rd_base256_size } -- Best regards, Michał Górny
Attachment:
signature.asc
Description: This is a digitally signed message part