tar: accomodate non-terminated tar.chksum fields as seen from github.com
No binary (build failed)
—
(log yok)
| @@ -152,6 +152,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| file_header_t *file_header = archive_handle->file_header; | |
| struct tar_header_t tar; | |
| char *cp; | |
| + | int tar_typeflag; /* can be "char", "int" seems give smaller code */ |
| int i, sum_u, sum; | |
| #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY | |
| int sum_s; | |
| @@ -253,10 +254,10 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| * POSIX says that checksum is done on unsigned bytes, but | |
| * Sun and HP-UX gets it wrong... more details in | |
| * GNU tar source. */ | |
| + | sum_u = ' ' * sizeof(tar.chksum); |
| #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY | |
| - | sum_s = ' ' * sizeof(tar.chksum); |
| + | sum_s = sum_u; |
| #endif | |
| - | sum_u = ' ' * sizeof(tar.chksum); |
| for (i = 0; i < 148; i++) { | |
| sum_u += ((unsigned char*)&tar)[i]; | |
| #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY | |
| @@ -269,27 +270,22 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| sum_s += ((signed char*)&tar)[i]; | |
| #endif | |
| } | |
| - | /* This field does not need special treatment (getOctal) */ |
| - | { |
| - | char *endp; /* gcc likes temp var for &endp */ |
| - | sum = strtoul(tar.chksum, &endp, 8); |
| - | if ((*endp != '\0' && *endp != ' ') |
| - | || (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) |
| - | ) { |
| - | bb_error_msg_and_die("invalid tar header checksum"); |
| - | } |
| - | } |
| - | /* don't use xstrtoul, tar.chksum may have leading spaces */ |
| - | sum = strtoul(tar.chksum, NULL, 8); |
| - | if (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) { |
| + | /* Most tarfiles have tar.chksum NUL or space terminated, but |
| + | * github.com decided to be "special" and have unterminated field: |
| + | * 0090: 30343300 30303031 33323731 30000000 |043.000132710...| |
| + | * ^^^^^^^^| |
| + | * Need to use GET_OCTAL. This overwrites tar.typeflag ---+ |
| + | * (the '0' char immediately after chksum in example above) with NUL. |
| + | */ |
| + | tar_typeflag = (uint8_t)tar.typeflag; /* save it */ |
| + | sum = GET_OCTAL(tar.chksum); |
| + | if (sum_u != sum |
| + | IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum) |
| + | ) { |
| bb_error_msg_and_die("invalid tar header checksum"); | |
| } | |
| - | /* 0 is reserved for high perf file, treat as normal file */ |
| - | if (!tar.typeflag) tar.typeflag = '0'; |
| - | parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7'); |
| - | |
| - | /* getOctal trashes subsequent field, therefore we call it |
| + | /* GET_OCTAL trashes subsequent field, therefore we call it |
| * on fields in reverse order */ | |
| if (tar.devmajor[0]) { | |
| char t = tar.prefix[0]; | |
| @@ -299,6 +295,11 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| file_header->device = makedev(major, minor); | |
| tar.prefix[0] = t; | |
| } | |
| + | |
| + | /* 0 is reserved for high perf file, treat as normal file */ |
| + | if (tar_typeflag == '\0') tar_typeflag = '0'; |
| + | parse_names = (tar_typeflag >= '0' && tar_typeflag <= '7'); |
| + | |
| file_header->link_target = NULL; | |
| if (!p_linkname && parse_names && tar.linkname[0]) { | |
| file_header->link_target = xstrndup(tar.linkname, sizeof(tar.linkname)); | |
| @@ -332,7 +333,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| /* Set bits 12-15 of the files mode */ | |
| /* (typeflag was not trashed because chksum does not use getOctal) */ | |
| - | switch (tar.typeflag) { |
| + | switch (tar_typeflag) { |
| case '1': /* hardlink */ | |
| /* we mark hardlinks as regular files with zero size and a link name */ | |
| file_header->mode |= S_IFREG; | |
| @@ -381,7 +382,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| case 'x': { /* pax extended header */ | |
| if ((uoff_t)file_header->size > 0xfffff) /* paranoia */ | |
| goto skip_ext_hdr; | |
| - | process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g')); |
| + | process_pax_hdr(archive_handle, file_header->size, (tar_typeflag == 'g')); |
| goto again_after_align; | |
| #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | |
| /* See http://www.gnu.org/software/tar/manual/html_node/Extensions.html */ | |
| @@ -419,7 +420,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| skip_ext_hdr: | |
| { | |
| off_t sz; | |
| - | bb_error_msg("warning: skipping header '%c'", tar.typeflag); |
| + | bb_error_msg("warning: skipping header '%c'", tar_typeflag); |
| sz = (file_header->size + 511) & ~(off_t)511; | |
| archive_handle->offset += sz; | |
| sz >>= 9; /* sz /= 512 but w/o contortions for signed div */ | |
| @@ -429,7 +430,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |
| goto again_after_align; | |
| } | |
| default: | |
| - | bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag); |
| + | bb_error_msg_and_die("unknown typeflag: 0x%x", tar_typeflag); |
| } | |
| #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | |