Re: [v2 PATCH] builtin: Fix octal escapes in dollar-single-quotes
"Kerin Millar" <[email protected]> Sun, 31 May 2026 07:08:51 +0100
| Newsgroups | org.kernel.vger.dash |
|---|---|
| Message-ID | <[email protected]> |
On Sun, 31 May 2026, at 6:36 AM, Herbert Xu wrote: > Kerin Millar <[email protected]> wrote: >> >> The test suite of the gentoo-functions project recently uncovered a bug whereby octal sequences within dollar-single-quotes are evaluated as internal control characters, resulting in faulty decoding at best, and segmentation faults at worse. The attached patch rectifies this bug. > > Thanks for the patch. > > While looking at this I found out that the logic for \x is broken > as it always produces a multi-byte character even for the value of > \x81. > > So here is a rolled-up patch which fixes both problems. > > ---8<--- > From: Kerin Millar <[email protected]> > > The test suite of gentoo-functions recently uncovered a bug concerning > the handling of octal escape sequences within dollar-single-quotes. > Here is a reproducer: > > $ dash -c "x=\$'\\201'; printf '%s' \"\$x\"" | od -An -tx1 > 88 > > That is, despite the input being 0x81, the output is 0x88. Indeed, any > input between 0x81..0x88 is adversely affected and some - such as 0x82 - > induce a segfault. > > I noticed the following macros in src/parser.h: > > #define CTL_FIRST -127 /* first 'special' character */ > #define CTLESC -127 /* escape next character */ > #define CTL_LAST -120 /* last 'special' character */ > > Reinterpreted as unsigned char, the CTL_FIRST..CTL_LAST range maps > exactly to 0x81..0x88: > > $ perl -e 'printf "%x\0", $_ & 0xFF for -127..-120' | xargs -0 > 81 82 83 84 85 86 87 88 > > From this, I was able to deduce that all of these bytes must be preceded > by CTLESC in order to be taken literally. Make it so. > > Link: > https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=947090fb7704 > Signed-off-by: Kerin Millar <[email protected]> > > The escape should also be applied to \x sequences. In fact \x81 > was totally broken as it produced a multi-byte character. Fix this > by merging these two code paths. > > Signed-off-by: Herbert Xu <[email protected]> > > diff --git a/src/bltin/printf.c b/src/bltin/printf.c > index 106aecd..671e781 100644 > --- a/src/bltin/printf.c > +++ b/src/bltin/printf.c > @@ -332,6 +332,7 @@ unsigned conv_escape(char *str0, char *out0, bool mbchar) > char *out = out0; > char *str = str0; > unsigned value; > + int och; > int ch; > > ch = *str; > @@ -359,12 +360,18 @@ unsigned conv_escape(char *str0, char *out0, bool mbchar) > } > > str--; > + > +check_value: > + if (mbchar && (signed char)value >= CTL_FIRST && > + (signed char)value <= CTL_LAST) > + USTPUTC(CTLESC, out); > break; > > case 'x': > ch = 2; > > hex: > + och = ch; > value = 0; > do { > int c = *++str; > @@ -391,6 +398,9 @@ hex: > if (value < 0x80) > break; > > + if (och <= 2) > + goto check_value; > + > if (value < 0x110000) { > int mboff = (mbchar - 1) * 2; > unsigned uni = value; Thank you, that looks great. It occurs to me that I should be able to incorporate some sort of test case for this because, without the revised patch, this happens: $ printf %s $'\x81' | wc -c 2 -- Kerin Millar