Summary
When FAKETIME is unset, libfaketime reads FAKETIME_TIMESTAMP_FILE, ~/.faketimerc, or /etc/faketimerc and runs the buffer through prepare_config_contents() before parse_ft_string().
That helper is documented as removing \r/\n and dropping lines that start with # or ;. Two independent bugs in the in-place scan mean that documented behaviour does not hold:
- The “skip newline” test reads
*write_position, not *read_position. After a comment line, the original LF is copied to the front of the buffer; the write pointer then lands on that same LF still sitting in the uncompacted tail, and the rest of the timestamp is dropped. parse_ft_string() then exit()s.
- Only
\n clears in_comment / beginning_of_line. A CR-terminated comment swallows everything after it. The result is an empty string, which parse_ft_string() treats as “unchanged” and returns without error — fake time is never applied.
A one-line file without comments still works (that is what README describes). The implementation, however, explicitly supports comments and multi-line files, and any such file is enough to break the default config path.
Present on 0.9.12 (bbb6c08).
Code
/* src/libfaketime.c — prepare_config_contents */
if (*read_position == '\n') { /* CR does not end a comment */
in_comment = false;
beginning_of_line = true;
}
if (!in_comment && *read_position != '\r' && *write_position != '\n') {
*write_position = *read_position; /* should test *read_position */
write_position++;
}
Call chain when FAKETIME is unset:
ftpl_really_init / fake_clock_gettime
-> read_config_file() /* :3516 */
open ~/.faketimerc or /etc/faketimerc
prepare_config_contents() /* :3543 */
parse_ft_string() /* :3544; empty string returns at :2760 */
Worked example (LF + comment), buffer # comment\n@2005-03-29 14:14:14\n:
- Comment-line LF is copied to index 0 because
*write_position is still '#'.
- After copying
@2005-03, the write pointer sits on the original LF at index 9.
*write_position != '\n' is false, so 29 14:14:14 is never copied.
- Result:
"\n@2005-03". parse_ft_string() prints failed to parse FAKETIME timestamp and exits.
CR + comment (# comment\r@2005-03-29 14:14:14\r) leaves an empty string. parse_ft_string sees it equal to the initial user_faked_time_saved and returns; the process keeps the real clock.
Reproduce
make -C src
WORKDIR=$(mktemp -d)
# 1) LF file with a comment line — parse fails and the process exits
printf '# comment\n@2005-03-29 14:14:14\n' > "$WORKDIR/.faketimerc"
env -u FAKETIME -u FAKETIME_TIMESTAMP_FILE HOME="$WORKDIR" \
LD_PRELOAD=./src/libfaketime.so.1 date
# libfaketime: In parse_ft_string(), failed to parse FAKETIME timestamp.
# Please check specification \n@2005-03 with format %Y-%m-%d %T
# 2) same timestamp, no comment — fakes correctly
printf '@2005-03-29 14:14:14\n' > "$WORKDIR/.faketimerc"
env -u FAKETIME -u FAKETIME_TIMESTAMP_FILE HOME="$WORKDIR" \
LD_PRELOAD=./src/libfaketime.so.1 date +%s
# 1112076854
# 3) CR-terminated comment — no error, real clock is used
printf '# comment\r@2005-03-29 14:14:14\r' > "$WORKDIR/.faketimerc"
env -u FAKETIME -u FAKETIME_TIMESTAMP_FILE HOME="$WORKDIR" \
LD_PRELOAD=./src/libfaketime.so.1 date +%s
# real epoch (not 1112076854)
# 4) CR timestamp without comment — still fakes (control)
printf '@2005-03-29 14:14:14\r' > "$WORKDIR/.faketimerc"
env -u FAKETIME -u FAKETIME_TIMESTAMP_FILE HOME="$WORKDIR" \
LD_PRELOAD=./src/libfaketime.so.1 date +%s
# 1112076854
Use a throwaway HOME; do not overwrite a real ~/.faketimerc.
Suggested fix
Test the input character, and treat both \n and \r as end-of-line (consume CRLF as one separator):
--- a/src/libfaketime.c
+++ b/src/libfaketime.c
@@ -3449,16 +3449,21 @@ static void prepare_config_contents(char *contents)
in_comment = true;
}
beginning_of_line = false;
- if (*read_position == '\n') {
+ if (*read_position == '\n' || *read_position == '\r') {
in_comment = false;
beginning_of_line = true;
+ if (*read_position == '\r' && *(read_position + 1) == '\n')
+ read_position++;
}
- if (!in_comment && *read_position != '\r' && *write_position != '\n') {
+ if (!in_comment && *read_position != '\r' && *read_position != '\n') {
*write_position = *read_position;
write_position++;
}
read_position++;
After this, a leading # line is dropped and the timestamp line is kept, for LF, CR, and CRLF.
Environment
wolfcw/libfaketime 0.9.12 (bbb6c08)
- Linux x86_64, stock
make (-DFAKE_PTHREAD -DFAKE_STAT -DFAKE_UTIME …)
Summary
When
FAKETIMEis unset, libfaketime readsFAKETIME_TIMESTAMP_FILE,~/.faketimerc, or/etc/faketimercand runs the buffer throughprepare_config_contents()beforeparse_ft_string().That helper is documented as removing
\r/\nand dropping lines that start with#or;. Two independent bugs in the in-place scan mean that documented behaviour does not hold:*write_position, not*read_position. After a comment line, the original LF is copied to the front of the buffer; the write pointer then lands on that same LF still sitting in the uncompacted tail, and the rest of the timestamp is dropped.parse_ft_string()thenexit()s.\nclearsin_comment/beginning_of_line. A CR-terminated comment swallows everything after it. The result is an empty string, whichparse_ft_string()treats as “unchanged” and returns without error — fake time is never applied.A one-line file without comments still works (that is what README describes). The implementation, however, explicitly supports comments and multi-line files, and any such file is enough to break the default config path.
Present on 0.9.12 (
bbb6c08).Code
Call chain when
FAKETIMEis unset:Worked example (LF + comment), buffer
# comment\n@2005-03-29 14:14:14\n:*write_positionis still'#'.@2005-03, the write pointer sits on the original LF at index 9.*write_position != '\n'is false, so29 14:14:14is never copied."\n@2005-03".parse_ft_string()printsfailed to parse FAKETIME timestampand exits.CR + comment (
# comment\r@2005-03-29 14:14:14\r) leaves an empty string.parse_ft_stringsees it equal to the initialuser_faked_time_savedand returns; the process keeps the real clock.Reproduce
Use a throwaway
HOME; do not overwrite a real~/.faketimerc.Suggested fix
Test the input character, and treat both
\nand\ras end-of-line (consume CRLF as one separator):After this, a leading
#line is dropped and the timestamp line is kept, for LF, CR, and CRLF.Environment
wolfcw/libfaketime0.9.12 (bbb6c08)make(-DFAKE_PTHREAD -DFAKE_STAT -DFAKE_UTIME …)