#1144850 cron: crontab(1) silently truncates file path arguments >=100 chars

Package:
cron
Source:
cron
Description:
process scheduling daemon
Submitter:
Date:
2026-08-19 10:41:02 UTC
Severity:
normal
Tags:
#1144850#5
Date:
2026-08-19 10:39:08 UTC
From:
To:
Dear Maintainer,

`crontab <file>` silently truncates the file path argument at 100 chars
instead of rejecting a too-long path, then fails against the truncated
path with a misleading "No such file or directory" or "Not a regular
file" error that doesn't match the path the user actually passed.

Root cause: cron.h defines

    #define MAX_FNAME 100 /* max length of internally generated fn */

That buffer is meant for cron's own internally-generated filenames, but
crontab.c reuses the same constant for the buffer that holds the
user-supplied CLI path argument:

    static char Filename[MAX_FNAME];   /* crontab.c:60 */
    ...
    (void) strncpy (Filename, argv[optind], (sizeof Filename)-1);
    Filename[(sizeof Filename)-1] = '\0';   /* crontab.c:255-256 */

There is no length check before the strncpy, so any path >=100 chars
(99 usable chars + NUL) is silently truncated. The program then tries
to open/stat the truncated path, which either doesn't exist (ENOENT,
"No such file or directory") or happens to resolve to an existing
directory ("Not a regular file") -- neither error message reflects
what actually went wrong, which makes this very confusing to debug:
the file genuinely exists at the path the user gave, but crontab is
silently operating on a different, truncated path.

Reproduction (byte-precision, confirmed on 3.0pl1-197):

    $ f=/tmp/$(python3 -c "print('a'*86)")/x   # 99 chars total
    $ mkdir -p "$(dirname "$f")"; echo '* * * * * true' > "$f"
    $ crontab "$f"; echo $?
    0

    $ f=/tmp/$(python3 -c "print('a'*87)")/x   # 100 chars total
    $ mkdir -p "$(dirname "$f")"; echo '* * * * * true' > "$f"
    $ crontab "$f"
    /tmp/aaaa....aaa: Not a regular file.
    (the printed path is the truncated 99-char string, cut off before
    the trailing "/x" -- it happens to equal the parent directory,
    which is why stat() succeeds but S_ISREG() fails)

Proposed fix: reject with a clear error instead of truncating silently.
Minimal patch against crontab.c, just before the existing strncpy at
line 255:
--- a/crontab.c
+++ b/crontab.c
@@ -252,6 +252,11 @@
 		if (argv[optind] != NULL) {
 			Option = opt_replace;
+			if (strlen(argv[optind]) >= sizeof(Filename)) {
+				fprintf(stderr,
+					"%s: pathname too long (max %zu chars)\n",
+					argv[optind], sizeof(Filename) - 1);
+				exit(ERROR_EXIT);
+			}
 			(void) strncpy (Filename, argv[optind], (sizeof Filename)-1);
 			Filename[(sizeof Filename)-1] = '\0';

This preserves the existing MAX_FNAME=100 limit used elsewhere in the
codebase for internally-generated filenames, and just stops it from
silently truncating a user-supplied path into a different, unintended
path.