]> git.ipfire.org Git - thirdparty/xz.git/commitdiff
xz: Use tuklib_mbstr_nonprint
authorLasse Collin <lasse.collin@tukaani.org>
Wed, 18 Dec 2024 12:00:09 +0000 (14:00 +0200)
committerLasse Collin <lasse.collin@tukaani.org>
Wed, 18 Dec 2024 15:09:32 +0000 (17:09 +0200)
Call tuklib_mask_nonprint() on filenames and also on a few other
strings from the command line too.

The filename printed by "xz --robot --list" (in list.c) is also masked.
It's good to get rid of tabs and newlines which would desync the output
but masking other chars wouldn't be strictly necessary. It might matter
with sensible filenames if LC_CTYPE is "C" (when iswprint() might reject
non-ASCII chars) and a script wants to read a filename from xz's output.
Hopefully it's an unusual enough corner case to not be a real problem.

CMakeLists.txt
src/xz/Makefile.am
src/xz/coder.c
src/xz/file_io.c
src/xz/list.c
src/xz/main.c
src/xz/message.c
src/xz/options.c
src/xz/private.h
src/xz/suffix.c

index 5e2c65354f4da677c388df739efe0786c57cbf0e..40828cd9294a9b0976264dc0600b03b1cf187355 100644 (file)
@@ -1989,6 +1989,8 @@ if(XZ_TOOL_XZ)
         src/common/sysdefs.h
         src/common/tuklib_common.h
         src/common/tuklib_config.h
+        src/common/tuklib_mbstr_nonprint.c
+        src/common/tuklib_mbstr_nonprint.h
         src/common/tuklib_exit.c
         src/common/tuklib_exit.h
         src/common/tuklib_gettext.h
index 38cdf13b8c90422f8de02cf222f0d46711381cf6..38d75cede50b3729e3a2608f9660e8cbe9fe44bf 100644 (file)
@@ -33,6 +33,7 @@ xz_SOURCES = \
        ../common/tuklib_progname.c \
        ../common/tuklib_exit.c \
        ../common/tuklib_mbstr_fw.c \
+       ../common/tuklib_mbstr_nonprint.c \
        ../common/tuklib_mbstr_width.c \
        ../common/tuklib_mbstr_wrap.c
 
index 5e41f0df6802473e19abdb395cd706b01ce2fe09..c9899abbe9688e264059da14eba639bc36679d4d 100644 (file)
@@ -1003,8 +1003,9 @@ coder_init(file_pair *pair)
                        strm.avail_out = 0;
                        while ((ret = lzma_code(&strm, LZMA_RUN))
                                        == LZMA_UNSUPPORTED_CHECK)
-                               message_warning(_("%s: %s"), pair->src_name,
-                                               message_strm(ret));
+                               message_warning(_("%s: %s"),
+                                       tuklib_mask_nonprint(pair->src_name),
+                                       message_strm(ret));
 
                        // With --single-stream lzma_code won't wait for
                        // LZMA_FINISH and thus it can return LZMA_STREAM_END
@@ -1019,7 +1020,9 @@ coder_init(file_pair *pair)
        }
 
        if (ret != LZMA_OK) {
-               message_error(_("%s: %s"), pair->src_name, message_strm(ret));
+               message_error(_("%s: %s"),
+                               tuklib_mask_nonprint(pair->src_name),
+                               message_strm(ret));
                if (ret == LZMA_MEMLIMIT_ERROR)
                        message_mem_needed(V_ERROR, lzma_memusage(&strm));
 
@@ -1320,11 +1323,13 @@ coder_normal(file_pair *pair)
                        // wrong and we print an error. Otherwise it's just
                        // a warning and coding can continue.
                        if (stop) {
-                               message_error(_("%s: %s"), pair->src_name,
-                                               message_strm(ret));
+                               message_error(_("%s: %s"),
+                                       tuklib_mask_nonprint(pair->src_name),
+                                       message_strm(ret));
                        } else {
-                               message_warning(_("%s: %s"), pair->src_name,
-                                               message_strm(ret));
+                               message_warning(_("%s: %s"),
+                                       tuklib_mask_nonprint(pair->src_name),
+                                       message_strm(ret));
 
                                // When compressing, all possible errors set
                                // stop to true.
index 678a9a5ca8604a9a829c2a5ec740b07c8da196df..978f19b18fd95fe24db2d4eec43f7f6805f11968 100644 (file)
@@ -205,8 +205,9 @@ io_wait(file_pair *pair, int timeout, bool is_reading)
                                continue;
 
                        message_error(_("%s: poll() failed: %s"),
-                                       is_reading ? pair->src_name
-                                               : pair->dest_name,
+                                       tuklib_mask_nonprint(is_reading
+                                               ? pair->src_name
+                                               : pair->dest_name),
                                        strerror(errno));
                        return IO_WAIT_ERROR;
                }
@@ -272,14 +273,15 @@ io_unlink(const char *name, const struct stat *known_st)
                // of the original file, and in that case it obviously
                // shouldn't be removed.
                message_warning(_("%s: File seems to have been moved, "
-                               "not removing"), name);
+                               "not removing"), tuklib_mask_nonprint(name));
        else
 #endif
                // There's a race condition between lstat() and unlink()
                // but at least we have tried to avoid removing wrong file.
                if (unlink(name))
                        message_warning(_("%s: Cannot remove: %s"),
-                                       name, strerror(errno));
+                                       tuklib_mask_nonprint(name),
+                                       strerror(errno));
 
        return;
 }
@@ -305,7 +307,8 @@ io_copy_attrs(const file_pair *pair)
        if (fchown(pair->dest_fd, pair->src_st.st_uid, (gid_t)(-1))
                        && warn_fchown)
                message_warning(_("%s: Cannot set the file owner: %s"),
-                               pair->dest_name, strerror(errno));
+                               tuklib_mask_nonprint(pair->dest_name),
+                               strerror(errno));
 
        mode_t mode;
 
@@ -318,7 +321,8 @@ io_copy_attrs(const file_pair *pair)
                        && fchown(pair->dest_fd, (uid_t)(-1),
                                pair->src_st.st_gid)) {
                message_warning(_("%s: Cannot set the file group: %s"),
-                               pair->dest_name, strerror(errno));
+                               tuklib_mask_nonprint(pair->dest_name),
+                               strerror(errno));
                // We can still safely copy some additional permissions:
                // 'group' must be at least as strict as 'other' and
                // also vice versa.
@@ -337,7 +341,8 @@ io_copy_attrs(const file_pair *pair)
 
        if (fchmod(pair->dest_fd, mode))
                message_warning(_("%s: Cannot set the file permissions: %s"),
-                               pair->dest_name, strerror(errno));
+                               tuklib_mask_nonprint(pair->dest_name),
+                               strerror(errno));
 #endif
 
        // Copy the timestamps. We have several possible ways to do this, of
@@ -515,13 +520,15 @@ io_open_src_real(file_pair *pair)
        if (!follow_symlinks) {
                struct stat st;
                if (lstat(pair->src_name, &st)) {
-                       message_error(_("%s: %s"), pair->src_name,
+                       message_error(_("%s: %s"),
+                                       tuklib_mask_nonprint(pair->src_name),
                                        strerror(errno));
                        return true;
 
                } else if (S_ISLNK(st.st_mode)) {
                        message_warning(_("%s: Is a symbolic link, "
-                                       "skipping"), pair->src_name);
+                                       "skipping"),
+                                       tuklib_mask_nonprint(pair->src_name));
                        return true;
                }
        }
@@ -583,13 +590,15 @@ io_open_src_real(file_pair *pair)
 
                if (was_symlink)
                        message_warning(_("%s: Is a symbolic link, "
-                                       "skipping"), pair->src_name);
+                                       "skipping"),
+                                       tuklib_mask_nonprint(pair->src_name));
                else
 #endif
                        // Something else than O_NOFOLLOW failing
                        // (assuming that the race conditions didn't
                        // confuse us).
-                       message_error(_("%s: %s"), pair->src_name,
+                       message_error(_("%s: %s"),
+                                       tuklib_mask_nonprint(pair->src_name),
                                        strerror(errno));
 
                return true;
@@ -612,13 +621,13 @@ io_open_src_real(file_pair *pair)
 
        if (S_ISDIR(pair->src_st.st_mode)) {
                message_warning(_("%s: Is a directory, skipping"),
-                               pair->src_name);
+                               tuklib_mask_nonprint(pair->src_name));
                goto error;
        }
 
        if (reg_files_only && !S_ISREG(pair->src_st.st_mode)) {
                message_warning(_("%s: Not a regular file, skipping"),
-                               pair->src_name);
+                               tuklib_mask_nonprint(pair->src_name));
                goto error;
        }
 
@@ -636,21 +645,21 @@ io_open_src_real(file_pair *pair)
                        // explicitly in io_copy_attr().
                        message_warning(_("%s: File has setuid or "
                                        "setgid bit set, skipping"),
-                                       pair->src_name);
+                                       tuklib_mask_nonprint(pair->src_name));
                        goto error;
                }
 
                if (pair->src_st.st_mode & S_ISVTX) {
                        message_warning(_("%s: File has sticky bit "
                                        "set, skipping"),
-                                       pair->src_name);
+                                       tuklib_mask_nonprint(pair->src_name));
                        goto error;
                }
 
                if (pair->src_st.st_nlink > 1) {
                        message_warning(_("%s: Input file has more "
-                                       "than one hard link, "
-                                       "skipping"), pair->src_name);
+                                       "than one hard link, skipping"),
+                                       tuklib_mask_nonprint(pair->src_name));
                        goto error;
                }
        }
@@ -679,7 +688,8 @@ io_open_src_real(file_pair *pair)
        return false;
 
 error_msg:
-       message_error(_("%s: %s"), pair->src_name, strerror(errno));
+       message_error(_("%s: %s"), tuklib_mask_nonprint(pair->src_name),
+                       strerror(errno));
 error:
        (void)close(pair->src_fd);
        return true;
@@ -816,7 +826,8 @@ io_open_dest_real(file_pair *pair)
                        if (st.st_dev == -1) {
                                message_error("%s: Refusing to write to "
                                                "a DOS special file",
-                                               pair->dest_name);
+                                               tuklib_mask_nonprint(
+                                                       pair->dest_name));
                                free(pair->dest_name);
                                return true;
                        }
@@ -826,7 +837,8 @@ io_open_dest_real(file_pair *pair)
                                        && st.st_ino == pair->src_st.st_ino) {
                                message_error("%s: Output file is the same "
                                                "as the input file",
-                                               pair->dest_name);
+                                               tuklib_mask_nonprint(
+                                                       pair->dest_name));
                                free(pair->dest_name);
                                return true;
                        }
@@ -836,7 +848,8 @@ io_open_dest_real(file_pair *pair)
                // If --force was used, unlink the target file first.
                if (opt_force && unlink(pair->dest_name) && errno != ENOENT) {
                        message_error(_("%s: Cannot remove: %s"),
-                                       pair->dest_name, strerror(errno));
+                                       tuklib_mask_nonprint(pair->dest_name),
+                                       strerror(errno));
                        free(pair->dest_name);
                        return true;
                }
@@ -851,7 +864,8 @@ io_open_dest_real(file_pair *pair)
                pair->dest_fd = open(pair->dest_name, flags, mode);
 
                if (pair->dest_fd == -1) {
-                       message_error(_("%s: %s"), pair->dest_name,
+                       message_error(_("%s: %s"),
+                                       tuklib_mask_nonprint(pair->dest_name),
                                        strerror(errno));
                        free(pair->dest_name);
                        return true;
@@ -882,7 +896,7 @@ io_open_dest_real(file_pair *pair)
        else if (pair->dest_fd != STDOUT_FILENO
                        && !S_ISREG(pair->dest_st.st_mode)) {
                message_error("%s: Destination is not a regular file",
-                               pair->dest_name);
+                               tuklib_mask_nonprint(pair->dest_name));
 
                // dest_fd needs to be reset to -1 to keep io_close() working.
                (void)close(pair->dest_fd);
@@ -1005,7 +1019,8 @@ io_close_dest(file_pair *pair, bool success)
 
        if (close(pair->dest_fd)) {
                message_error(_("%s: Closing the file failed: %s"),
-                               pair->dest_name, strerror(errno));
+                               tuklib_mask_nonprint(pair->dest_name),
+                               strerror(errno));
 
                // Closing destination file failed, so we cannot trust its
                // contents. Get rid of junk:
@@ -1042,7 +1057,8 @@ io_close(file_pair *pair, bool success)
                                SEEK_CUR) == -1) {
                        message_error(_("%s: Seeking failed when trying "
                                        "to create a sparse file: %s"),
-                                       pair->dest_name, strerror(errno));
+                                       tuklib_mask_nonprint(pair->dest_name),
+                                       strerror(errno));
                        success = false;
                } else {
                        const uint8_t zero[1] = { '\0' };
@@ -1141,7 +1157,8 @@ io_read(file_pair *pair, io_buf *buf, size_t size)
 #endif
 
                        message_error(_("%s: Read error: %s"),
-                                       pair->src_name, strerror(errno));
+                                       tuklib_mask_nonprint(pair->src_name),
+                                       strerror(errno));
 
                        return SIZE_MAX;
                }
@@ -1171,7 +1188,8 @@ io_seek_src(file_pair *pair, uint64_t pos)
 
        if (lseek(pair->src_fd, (off_t)(pos), SEEK_SET) == -1) {
                message_error(_("%s: Error seeking the file: %s"),
-                               pair->src_name, strerror(errno));
+                               tuklib_mask_nonprint(pair->src_name),
+                               strerror(errno));
                return true;
        }
 
@@ -1195,7 +1213,7 @@ io_pread(file_pair *pair, io_buf *buf, size_t size, uint64_t pos)
 
        if (amount != size) {
                message_error(_("%s: Unexpected end of file"),
-                               pair->src_name);
+                               tuklib_mask_nonprint(pair->src_name));
                return true;
        }
 
@@ -1254,7 +1272,8 @@ io_write_buf(file_pair *pair, const uint8_t *buf, size_t size)
                        // user_abort, and get EPIPE here.
                        if (errno != EPIPE)
                                message_error(_("%s: Write error: %s"),
-                                       pair->dest_name, strerror(errno));
+                                       tuklib_mask_nonprint(pair->dest_name),
+                                       strerror(errno));
 
                        return true;
                }
@@ -1304,7 +1323,9 @@ io_write(file_pair *pair, const io_buf *buf, size_t size)
                                        SEEK_CUR) == -1) {
                                message_error(_("%s: Seeking failed when "
                                                "trying to create a sparse "
-                                               "file: %s"), pair->dest_name,
+                                               "file: %s"),
+                                               tuklib_mask_nonprint(
+                                                       pair->dest_name),
                                                strerror(errno));
                                return true;
                        }
index 40b0281a18324c6b1df7fd93565d261f6d988f22..3758894363d5dbcd38ce5cee74c6b77ece4d937c 100644 (file)
@@ -347,13 +347,14 @@ static bool
 parse_indexes(xz_file_info *xfi, file_pair *pair)
 {
        if (pair->src_st.st_size <= 0) {
-               message_error(_("%s: File is empty"), pair->src_name);
+               message_error(_("%s: File is empty"),
+                               tuklib_mask_nonprint(pair->src_name));
                return true;
        }
 
        if (pair->src_st.st_size < 2 * LZMA_STREAM_HEADER_SIZE) {
                message_error(_("%s: Too small to be a valid .xz file"),
-                               pair->src_name);
+                               tuklib_mask_nonprint(pair->src_name));
                return true;
        }
 
@@ -365,7 +366,9 @@ parse_indexes(xz_file_info *xfi, file_pair *pair)
                        hardware_memlimit_get(MODE_LIST),
                        (uint64_t)(pair->src_st.st_size));
        if (ret != LZMA_OK) {
-               message_error(_("%s: %s"), pair->src_name, message_strm(ret));
+               message_error(_("%s: %s"),
+                               tuklib_mask_nonprint(pair->src_name),
+                               message_strm(ret));
                return true;
        }
 
@@ -411,7 +414,8 @@ parse_indexes(xz_file_info *xfi, file_pair *pair)
                }
 
                default:
-                       message_error(_("%s: %s"), pair->src_name,
+                       message_error(_("%s: %s"),
+                                       tuklib_mask_nonprint(pair->src_name),
                                        message_strm(ret));
 
                        // If the error was too low memory usage limit,
@@ -473,7 +477,8 @@ parse_block_header(file_pair *pair, const lzma_index_iter *iter,
                break;
 
        case LZMA_OPTIONS_ERROR:
-               message_error(_("%s: %s"), pair->src_name,
+               message_error(_("%s: %s"),
+                               tuklib_mask_nonprint(pair->src_name),
                                message_strm(LZMA_OPTIONS_ERROR));
                return true;
 
@@ -587,7 +592,8 @@ parse_block_header(file_pair *pair, const lzma_index_iter *iter,
 
        // Check if the stringification succeeded.
        if (str_ret != LZMA_OK) {
-               message_error(_("%s: %s"), pair->src_name,
+               message_error(_("%s: %s"),
+                               tuklib_mask_nonprint(pair->src_name),
                                message_strm(str_ret));
                return true;
        }
@@ -596,7 +602,8 @@ parse_block_header(file_pair *pair, const lzma_index_iter *iter,
 
 data_error:
        // Show the error message.
-       message_error(_("%s: %s"), pair->src_name,
+       message_error(_("%s: %s"),
+                       tuklib_mask_nonprint(pair->src_name),
                        message_strm(LZMA_DATA_ERROR));
        return true;
 }
@@ -744,7 +751,7 @@ print_info_basic(const xz_file_info *xfi, file_pair *pair)
        char checks[CHECKS_STR_SIZE];
        get_check_names(checks, lzma_index_checks(xfi->idx), false);
 
-       const char *cols[7] = {
+       const char *cols[6] = {
                uint64_to_str(lzma_index_stream_count(xfi->idx), 0),
                uint64_to_str(lzma_index_block_count(xfi->idx), 1),
                uint64_to_nicestr(lzma_index_file_size(xfi->idx),
@@ -754,7 +761,6 @@ print_info_basic(const xz_file_info *xfi, file_pair *pair)
                get_ratio(lzma_index_file_size(xfi->idx),
                        lzma_index_uncompressed_size(xfi->idx)),
                checks,
-               pair->src_name,
        };
        printf("%*s %*s  %*s  %*s  %*s  %-*s %s\n",
                        tuklib_mbstr_fw(cols[0], 5), cols[0],
@@ -763,7 +769,7 @@ print_info_basic(const xz_file_info *xfi, file_pair *pair)
                        tuklib_mbstr_fw(cols[3], 11), cols[3],
                        tuklib_mbstr_fw(cols[4], 5), cols[4],
                        tuklib_mbstr_fw(cols[5], 7), cols[5],
-                       cols[6]);
+                       tuklib_mask_nonprint(pair->src_name));
 
        return false;
 }
@@ -1048,7 +1054,11 @@ print_info_robot(xz_file_info *xfi, file_pair *pair)
        char checks[CHECKS_STR_SIZE];
        get_check_names(checks, lzma_index_checks(xfi->idx), false);
 
-       printf("name\t%s\n", pair->src_name);
+       // Robot mode has to mask at least some control chars to prevent
+       // the output from getting out of sync if filename is malicious.
+       // Masking all non-printable chars is more than we need but
+       // perhaps this is good enough in practice.
+       printf("name\t%s\n", tuklib_mask_nonprint(pair->src_name));
 
        printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
                        "\t%s\t%s\t%" PRIu64 "\n",
index 71b5ef7b7001ad028a69c88f59d2374f7f93240e..1b8b3788117269f5752bfe04867527f84eaea1a0 100644 (file)
@@ -87,7 +87,8 @@ read_name(const args_info *args)
                                continue;
 
                        message_error(_("%s: Error reading filenames: %s"),
-                                       args->files_name, strerror(errno));
+                               tuklib_mask_nonprint(args->files_name),
+                               strerror(errno));
                        return NULL;
                }
 
@@ -95,7 +96,8 @@ read_name(const args_info *args)
                        if (pos != 0)
                                message_error(_("%s: Unexpected end of input "
                                                "when reading filenames"),
-                                               args->files_name);
+                                               tuklib_mask_nonprint(
+                                                       args->files_name));
 
                        return NULL;
                }
@@ -120,7 +122,9 @@ read_name(const args_info *args)
                        message_error(_("%s: Null character found when "
                                        "reading filenames; maybe you meant "
                                        "to use '--files0' instead "
-                                       "of '--files'?"), args->files_name);
+                                       "of '--files'?"),
+                                       tuklib_mask_nonprint(
+                                               args->files_name));
                        return NULL;
                }
 
index 86a5cd3dc5ab00b30fbf9e7397ad4be0529ca199..1ff592ec4513c94ab10931362b69eaa81ee4b961 100644 (file)
@@ -196,10 +196,12 @@ print_filename(void)
                // If we don't know how many files there will be due
                // to usage of --files or --files0.
                if (files_total == 0)
-                       fprintf(file, "%s (%u)\n", filename,
+                       fprintf(file, "%s (%u)\n",
+                                       tuklib_mask_nonprint(filename),
                                        files_pos);
                else
-                       fprintf(file, "%s (%u/%u)\n", filename,
+                       fprintf(file, "%s (%u/%u)\n",
+                                       tuklib_mask_nonprint(filename),
                                        files_pos, files_total);
 
                signals_unblock();
@@ -648,7 +650,7 @@ progress_flush(bool finished)
                                cols[4]);
        } else {
                // The filename is always printed.
-               fprintf(stderr, _("%s: "), filename);
+               fprintf(stderr, _("%s: "), tuklib_mask_nonprint(filename));
 
                // Percentage is printed only if we didn't finish yet.
                if (!finished) {
index bc8bc1a6c36c53304d12c3db13781480943fd778..af0b28c592b92cf53a847d8316eb2569d434742d 100644 (file)
@@ -83,14 +83,15 @@ parse_options(const char *str, const option_map *opts,
 
                if (value == NULL || value[0] == '\0')
                        message_fatal(_("%s: Options must be 'name=value' "
-                                       "pairs separated with commas"), str);
+                                       "pairs separated with commas"),
+                                       tuklib_mask_nonprint(str));
 
                // Look for the option name from the option map.
                unsigned i = 0;
                while (true) {
                        if (opts[i].name == NULL)
                                message_fatal(_("%s: Invalid option name"),
-                                               name);
+                                               tuklib_mask_nonprint(name));
 
                        if (strcmp(name, opts[i].name) == 0)
                                break;
@@ -110,7 +111,7 @@ parse_options(const char *str, const option_map *opts,
 
                        if (opts[i].map[j].name == NULL)
                                message_fatal(_("%s: Invalid option value"),
-                                               value);
+                                               tuklib_mask_nonprint(value));
 
                        set(filter_options, i, opts[i].map[j].id, value);
 
@@ -244,7 +245,8 @@ tuklib_attr_noreturn
 static void
 error_lzma_preset(const char *valuestr)
 {
-       message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
+       message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"),
+                       tuklib_mask_nonprint(valuestr));
 }
 
 
index b370472e32c8887fecaf6ade19284747e1e32ddd..d351a995eec410dc2a723bc94afc65718e477767 100644 (file)
@@ -28,6 +28,7 @@
 #include "tuklib_gettext.h"
 #include "tuklib_progname.h"
 #include "tuklib_exit.h"
+#include "tuklib_mbstr_nonprint.h"
 #include "tuklib_mbstr.h"
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
index 1d548e485b8c5e0bfea601a2b762e78da8c48118..2fd4c7fc9573f48ea45af75171c22a7b12e5be55 100644 (file)
@@ -163,7 +163,7 @@ uncompressed_name(const char *src_name, const size_t src_len)
 
        if (new_len == 0) {
                message_warning(_("%s: Filename has an unknown suffix, "
-                               "skipping"), src_name);
+                               "skipping"), tuklib_mask_nonprint(src_name));
                return NULL;
        }
 
@@ -178,13 +178,14 @@ uncompressed_name(const char *src_name, const size_t src_len)
 }
 
 
-/// This message is needed in multiple places in compressed_name(),
-/// so the message has been put into its own function.
 static void
 msg_suffix(const char *src_name, const char *suffix)
 {
+       char *mem = NULL;
        message_warning(_("%s: File already has '%s' suffix, skipping"),
-                       src_name, suffix);
+                       tuklib_mask_nonprint(src_name),
+                       tuklib_mask_nonprint_r(suffix, &mem));
+       free(mem);
        return;
 }
 
@@ -390,7 +391,8 @@ suffix_set(const char *suffix)
        // Empty suffix and suffixes having a directory separator are
        // rejected. Such suffixes would break things later.
        if (suffix[0] == '\0' || has_dir_sep(suffix))
-               message_fatal(_("%s: Invalid filename suffix"), suffix);
+               message_fatal(_("%s: Invalid filename suffix"),
+                               tuklib_mask_nonprint(suffix));
 
        // Replace the old custom_suffix (if any) with the new suffix.
        free(custom_suffix);