Fedora Account System
Red Hat Associate
Red Hat Customer
Our lvm2 project dmstats tool started to fail after upgrade to recent glibc version in rawhide: glibc 2.42.9000-3.fc44.x86_64 Here is a simple reproducer: --- include <stdio.h> int main(void) { int x1, x2, x3, x4, r; char parsed[256] = { 0 }; char to_parse[] = "0: 0+1048576 1048576 dmstats -"; r = sscanf(to_parse, "%d: %d+%d %d %255c", &x1, &x2, &x3, &x4, parsed); printf("result: %d %s\n", r, parsed); return 0; } --- It's now after upgrade returning 'just' 4 instead of 5. However 'parsed' buffer still looks to be properly filled. Reproducible: Always
I'm afraid this is the behavior required by the C standard. It says “Matches a sequence of characters of exactly the number specified by the field width”, so if the field width is 255 as in your example, the input must contain 255 or characters for the match to succeed. Based on a quick Debian Code Search, this change looks rather disruptive. I'll mention this on libc-alpha.
Ah, my search was wrong. The problem is using "%255c" instead of "%255s". So the impact should be more limited than I expected. I do not plan to revert this in rawhide immediately.
There is quite a difference between %255c and %255s. The 's' stops with the first white space. The 'c' simply fills the buffer to its capacity with the white spaces included. This change basically modifies the behavior that's been there for ages. So IMHO a 'new letter' should be added for new behaviors. Also man page tells this: c Matches a sequence of characters whose length is specified by the maximum field width (default 1); 'maximum' != 'exactly'
Yes, whitespace treatment is another difference. If you simply want to read a varying number of characters, I think you are expected to use fgets. And the man page is just misleading. The glibc manual has always documented the current rawhide behavior: “ The ‘%c’ conversion is the simplest: it matches a fixed number of characters, always. The maximum field width says how many characters to read; if you don't specify the maximum, the default is 1. This conversion doesn't append a null character to the end of the text it reads. It also does not skip over initial whitespace characters. It reads precisely the next N characters, and fails if it cannot get that many. Since there is always a maximum field width with ‘%c’ (whether specified, or 1 by default), you can always prevent overflow by making the buffer long enough. ” It's just that this behavior wasn't implemented before. Other Linux libcs implement the standard behavior, too.
Well since this now kind of 'randomized' behavior of glibc makes the use of %Xc highly problematic - lvm2 libdm code simply dropped the use of this 'feature' - https://gitlab.com/lvmteam/lvm2/-/commit/be3224b3f535c2019d544e924712eadcfa93784f However surely old binary running with newer glibc will simply no longer work. So is there at least gcc versioning used so the older linked binaries do get old 'logic' - while new builds do get this new fancy one ?
Yes, upstream could at least provide a symbol version, but I'm not sure how easy that will be given the intertwined logic.
Have there been any other sightings of this issue? Or is lvm2 the only impacted component?
I need to send the man-pages correction upstream.
I sent a correction to the man-pages project: [PATCH] sscanf.3: Document exact matching for %c <https://lore.kernel.org/linux-man/lhu1pli6tsj.fsf@oldenburg.str.redhat.com/> On the glibc side, if more software is impacted by this correctness fix, we can consider adding a tunable (upstream) to restore the previous, standards-violating behavior.