To whom it may concern;
Spent some time chasing a red herring around the'get_suffix' function.
The patch included aims to simplify the logic considerably.
There are a number of interesting points to note:
Under normal situations (".gz") (could be modified by using -S) the
predicate:
<code>
if (z_len < suflen && strequ (z_suffix, *suf + suflen - z_len))
</code>
Will never be true. Modifications to the suffix string, possibly at
build time,
or on the command line will trigger the boolean to get set, executing:
<code>
known_suffixes[suffix_of_builtin
? sizeof known_suffixes / sizeof *known_suffixes - 2
: 0] = z_lower;
</code>
This will drop the duplicated pointer into the next-to-last element of
the array,
rather than the front. The const qualifier on known_suffixes on reading
that it is const but this is not the case.
strlen is a size_t, there may potentially be sign extension issues.
It could be possible to craft a pointer as an argument to a mapped
memory region if it was large enough to trigger a sign extension error,
however it's extremely unlikely.
Take care,
John SETH Thielemann
JOSTALY Technologies
https://www.jostaly.com
223-231-3511
diff -Naur gzip-1.12.vanilla/gzip.c gzip-1.12.patch/gzip.c
--- gzip-1.12.vanilla/gzip.c 2022-03-31 01:32:53.000000000 +0000
+++ gzip-1.12.patch/gzip.c 2024-08-16 17:51:58.393769421 +0000
@@ -313,7 +313,7 @@
local void treat_stdin (void);
local void treat_file (char *iname);
local int create_outfile (void);
-local char *get_suffix (char *name);
+local const char *get_suffix (char *name);
local int open_input_file (char *iname, struct stat *sbuf);
local void discard_input_bytes (size_t nbytes, unsigned int flags);
local int make_ofname (void);
@@ -1169,59 +1169,24 @@
* .??z suffix as indicating a compressed file; some people use .xyz
* to denote volume data.
*/
-local char *get_suffix(name)
- char *name;
-{
- int nlen, slen;
- char suffix[MAX_SUFFIX+3]; /* last chars of name, forced to lower case */
- static char const *known_suffixes[] =
- {NULL, ".gz", ".z", ".taz", ".tgz", "-gz", "-z", "_z",
+
+static char const *known_suffixes[] =
+ {NULL, ".gz", ".z", ".taz", ".tgz", "-gz", "-z", "_z",
#ifdef MAX_EXT_CHARS
- "z",
+ "z",
#endif
- NULL, NULL};
+ NULL, NULL};
+
+local const char *get_suffix(name)
+ char *name;
+{
char const **suf;
- bool suffix_of_builtin = false;
- /* Normally put Z_SUFFIX at the start of KNOWN_SUFFIXES, but if it
- is a suffix of one of them, put it at the end. */
for (suf = known_suffixes + 1; *suf; suf++)
- {
- size_t suflen = strlen (*suf);
- if (z_len < suflen && strequ (z_suffix, *suf + suflen - z_len))
- {
- suffix_of_builtin = true;
- break;
- }
- }
-
- char *z_lower = xstrdup(z_suffix);
- strlwr(z_lower);
- known_suffixes[suffix_of_builtin
- ? sizeof known_suffixes / sizeof *known_suffixes - 2
- : 0] = z_lower;
- suf = known_suffixes + suffix_of_builtin;
-
- nlen = strlen(name);
- if (nlen <= MAX_SUFFIX+2) {
- strcpy(suffix, name);
- } else {
- strcpy(suffix, name+nlen-MAX_SUFFIX-2);
- }
- strlwr(suffix);
- slen = strlen(suffix);
- char *match = NULL;
- do {
- int s = strlen(*suf);
- if (slen > s && ! ISSLASH (suffix[slen - s - 1])
- && strequ(suffix + slen - s, *suf)) {
- match = name+nlen-s;
- break;
- }
- } while (*++suf != NULL);
- free(z_lower);
+ if (z_len <= strnlen(*suf, 4) && strequ (z_suffix, *suf))
+ return *suf;
- return match;
+ return NULL;
}
@@ -1386,7 +1351,13 @@
*/
local int make_ofname()
{
- char *suff; /* ofname z suffix */
+ const char *suff; /* ofname z suffix */
+ const size_t len = strnlen(ifname, MAX_PATH_LEN);
+
+ if (len >= MAX_PATH_LEN) {
+ WARN((stderr, "%s: invalid length on input file name\n"));
+ return WARNING;
+ }
strcpy(ofname, ifname);
/* strip a version number if any and get the gzip suffix if present: */
@@ -1407,59 +1378,18 @@
}
return WARNING;
}
- /* Make a special case for .tgz and .taz: */
- strlwr(suff);
- if (strequ(suff, ".tgz") || strequ(suff, ".taz")) {
- strcpy(suff, ".tar");
- } else {
- *suff = '\0'; /* strip the z suffix */
- }
- /* ofname might be changed later if infile contains an original name */
- } else if (suff && ! force) {
- /* Avoid annoying messages with -r (see treat_dir()) */
- if (verbose || (!recursive && !quiet)) {
- /* Don't use WARN, as it affects exit status. */
- fprintf (stderr, "%s: %s already has %s suffix -- unchanged\n",
- program_name, ifname, suff);
- }
- return WARNING;
+ ofname[len - strnlen(suff, 4)] = 0;
} else {
- save_orig_name = 0;
-
-#ifdef NO_MULTIPLE_DOTS
- suff = strrchr(ofname, '.');
- if (suff == NULL) {
- if (sizeof ofname <= strlen (ofname) + 1)
- goto name_too_long;
- strcat(ofname, ".");
-# ifdef MAX_EXT_CHARS
- if (strequ(z_suffix, "z")) {
- if (sizeof ofname <= strlen (ofname) + 2)
- goto name_too_long;
- strcat(ofname, "gz"); /* enough room */
- return OK;
- }
- /* On the Atari and some versions of MSDOS,
- * ENAMETOOLONG does not work correctly. So we
- * must truncate here.
- */
- } else if (strlen(suff)-1 + z_len > MAX_SUFFIX) {
- suff[MAX_SUFFIX+1-z_len] = '\0';
- save_orig_name = 1;
-# endif
- }
-#endif /* NO_MULTIPLE_DOTS */
- if (sizeof ofname <= strlen (ofname) + z_len)
- goto name_too_long;
- strcat(ofname, z_suffix);
-
- } /* decompress ? */
- return OK;
+ const size_t suffLen = strnlen(suff, 4);
+ if ((len + suffLen) >= MAX_PATH_LEN) {
+ WARN((stderr, "%s: Append of suffix to input file name > MAX_PATH_LEN\n"));
+ return WARNING;
+ }
+ strncat(ofname, suff, suffLen);
+ }
- name_too_long:
- WARN ((stderr, "%s: %s: file name too long\n", program_name, ifname));
- return WARNING;
+ return OK;
}
/* Discard NBYTES input bytes from the input, or up through the next
@@ -1820,9 +1750,7 @@
char *name;
{
int len; /* length of name without z_suffix */
- char *trunc = NULL; /* character to be truncated */
int plen; /* current part length */
- int min_part = MIN_PART; /* current minimum part length */
char *p;
len = strlen(name);
@@ -1836,38 +1764,8 @@
if (! p)
gzip_error ("can't recover suffix\n");
*p = '\0';
- save_orig_name = 1;
-
- /* compress 1234567890.tar to 1234567890.tgz */
- if (len > 4 && strequ(p-4, ".tar")) {
- strcpy(p-4, ".tgz");
- return;
- }
- /* Try keeping short extensions intact:
- * 1234.678.012.gz -> 123.678.012.gz
- */
- do {
- p = last_component (name);
- while (*p) {
- plen = strcspn(p, PART_SEP);
- p += plen;
- if (plen > min_part) trunc = p-1;
- if (*p) p++;
- }
- } while (trunc == NULL && --min_part != 0);
-
- if (trunc != NULL) {
- do {
- trunc[0] = trunc[1];
- } while (*trunc++);
- trunc--;
- } else {
- trunc = strrchr(name, PART_SEP[0]);
- if (!trunc)
- gzip_error ("internal error in shorten_name");
- if (trunc[1] == '\0') trunc--; /* force truncation */
- }
- strcpy(trunc, z_suffix);
+ plen = strnlen(p, 4);
+ name[len - plen] = '\0';
}
/* ========================================================================