NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/60322: libedit readline compat function history_truncate_file() drops first line cookie



>Number:         60322
>Category:       lib
>Synopsis:       libedit readline compat function history_truncate_file() drops first line cookie
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jun 12 05:15:00 +0000 2026
>Originator:     Dominique Martinet
>Release:        3.1.20260512
>Organization:
Atmark Techno
>Environment:
produced with libedit portable ( https://thrysoee.dk/editline/ ) at least 3.1.20250104 through 3.1.20260512
>Description:
Calling history_truncate_file() with a non-trivial amount will drop the first line cookie ("_HiStOrY_V2_") from the history file, making the file no longer load afterwards (EINVAL)

This has been reported on postgres mailing lists here:
https://www.postgresql.org/message-id/CALaJbqz262ax%3Dt8W32oujxM%2B5Kn7Ov5nT%2BrZgaJn70CbRroFfQ%40mail.gmail.com

but I've also reproduced the problem with python (cpython) built with editline and running their test suite (see repeat instruction below)
>How-To-Repeat:
Using cpython from their git https://github.com/python/cpython as of a couple of day's ago main branch (84630e2cb90e "gh-136880: Add warning about PYTHONPATH (GH-151098)"):

  ./configure --with-readline=editline
  make
  ./python ./Lib/test/test_readline.py


should fail in a couple of places including the following
============8<==========
ERROR: test_write_read_limited_history (__main__.TestReadline.test_write_read_limited_history)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../cpython/./Lib/test/test_readline.py", line 415, in test_write_read_limited_history
    readline.read_history_file(TESTFN)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
OSError: [Errno 22] Invalid argument
============8<==========
>Fix:
As a first stopgap measure I've checked that just seeking after the cookie in history_truncate_file() made the problem go away:
============8<==========
diff --git a/src/readline.c b/src/readline.c
index 22e74d3f34c9..2f5ecfbfd164 100644
--- a/src/readline.c
+++ b/src/readline.c
@@ -1384,7 +1384,7 @@ history_truncate_file (const char *filename, int nlines)
 	if (ret || nlines > 0)
 		goto out3;
 
-	if (fseeko(fp, (off_t)0, SEEK_SET) == (off_t)-1) {
+	if (fseeko(fp, (off_t)strlen("_HiStOrY_V2_\n"), SEEK_SET) == (off_t)-1) {
 		ret = errno;
 		goto out3;
 	}
============8<==========

But that's not a proper fix, mostly because it just assumes the cookie is there and it hardcodes the value.

I see two obvious ways of dealing with this:
- since the cookie value is not shared (hist_cookie is a static variable in history.c), this code could just always keep the first line in place regardless of its value; it's a bit annoying to track yet another new line here and I'd need to introduce a couple of variables to do it but it's definitely a possibility
- make the hist_cookie variable non-static and use it as is in history_truncate_file(): the easiest fix I can see is keeping seek at 0 and unconditionally write the cookie first (might need to check we're actually truncating the file, or we'd risk having two cookies if count == 0?)

I'll be happy to test and send a patch with either version or anything else if someone has suggestions, or just leave it up to you.

Thanks!




Home | Main Index | Thread Index | Old Index