diff options
author | Bruno Haible <bruno@clisp.org> | 2023-03-12 11:45:11 +0100 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2023-03-14 02:57:28 +0100 |
commit | a03afb970c6abea8ac22b16df3cfc3419fdc795f (patch) | |
tree | 9ad765b97e5882350cc6b9115316f962e5487de4 | |
parent | 589731cc1b49bfbb2467b102fba8e1afd2277eef (diff) | |
download | gettext-a03afb970c6abea8ac22b16df3cfc3419fdc795f.tar.gz |
xgettext: Avoid a crash due to a python-brace-format string with a NUL byte.
* gettext-tools/src/format-python-brace.c: Improve comments.
(parse_directive): Don't read past the string end if c1 is NUL.
* gettext-tools/tests/xgettext-python-6: New file.
* gettext-tools/tests/Makefile.am (TESTS): Add it.
-rw-r--r-- | gettext-tools/src/format-python-brace.c | 52 | ||||
-rw-r--r-- | gettext-tools/tests/Makefile.am | 2 | ||||
-rwxr-xr-x | gettext-tools/tests/xgettext-python-6 | 23 |
3 files changed, 64 insertions, 13 deletions
diff --git a/gettext-tools/src/format-python-brace.c b/gettext-tools/src/format-python-brace.c index 969e58d11..229917952 100644 --- a/gettext-tools/src/format-python-brace.c +++ b/gettext-tools/src/format-python-brace.c @@ -32,17 +32,32 @@ #define _(str) gettext (str) -/* Python brace format strings are defined by PEP3101 together with - 'format' method of string class. +/* Python brace format strings are defined by PEP3101 together with the + 'format' method of the string class. A format string directive here consists of - an opening brace '{', - an identifier [_A-Za-z][_0-9A-Za-z]*|[0-9]+, - - an optional getattr ('.') or getitem ('['..']') operator with - an identifier as argument, - - an optional format specifier starting with ':', with a - (unnested) format string as argument, + - an optional sequence of + - getattr ('.' identifier) or + - getitem ('[' identifier ']') + operators, + - optionally, a ':' and a format specifier, where a format specifier is + - either a format directive of the form '{' ... '}' without a format + specifier, or + - of the form [[fill]align][sign][#][0][minimumwidth][.precision][type] + where + - the fill character is any character, + - the align flag is one of '<', '>', '=', '^', + - the sign is one of '+', '-', ' ', + - the # flag is '#', + - the 0 flag is '0', + - minimumwidth is a non-empty sequence of digits, + - precision is a non-empty sequence of digits, + - type is one of + - 'b', 'c', 'd', 'o', 'x', 'X', 'n' for integers, + - 'e', 'E', 'f', 'F', 'g', 'G', 'n', '%' for floating-point values, - a closing brace '}'. - Brace characters '{' and '}' can be escaped by doubles '{{' and '}}'. + Brace characters '{' and '}' can be escaped by doubling them: '{{' and '}}'. */ struct named_arg @@ -132,7 +147,8 @@ parse_directive (struct spec *spec, && !parse_numeric_field (spec, &format, translated, fdi, invalid_reason)) { *invalid_reason = - xasprintf (_("In the directive number %u, '%c' cannot start a field name."), spec->directives, *format); + xasprintf (_("In the directive number %u, '%c' cannot start a field name."), + spec->directives, *format); FDI_SET (format, FMTDIR_ERROR); return false; } @@ -151,7 +167,8 @@ parse_directive (struct spec *spec, invalid_reason)) { *invalid_reason = - xasprintf (_("In the directive number %u, '%c' cannot start a getattr argument."), spec->directives, *format); + xasprintf (_("In the directive number %u, '%c' cannot start a getattr argument."), + spec->directives, *format); FDI_SET (format, FMTDIR_ERROR); return false; } @@ -165,7 +182,8 @@ parse_directive (struct spec *spec, invalid_reason)) { *invalid_reason = - xasprintf (_("In the directive number %u, '%c' cannot start a getitem argument."), spec->directives, *format); + xasprintf (_("In the directive number %u, '%c' cannot start a getitem argument."), + spec->directives, *format); FDI_SET (format, FMTDIR_ERROR); return false; } @@ -187,7 +205,8 @@ parse_directive (struct spec *spec, if (!is_toplevel) { *invalid_reason = - xasprintf (_("In the directive number %u, no more nesting is allowed in a format specifier."), spec->directives); + xasprintf (_("In the directive number %u, no more nesting is allowed in a format specifier."), + spec->directives); FDI_SET (format, FMTDIR_ERROR); return false; } @@ -197,7 +216,7 @@ parse_directive (struct spec *spec, specifiers below, because otherwise we would need to evaluate Python expressions by ourselves: - - A nested format directive expanding to the whole string + - A nested format directive expanding to an argument - The Standard Format Specifiers, as described in PEP3101, not including a nested format directive */ format++; @@ -228,6 +247,15 @@ parse_directive (struct spec *spec, int c1, c2; c1 = format[0]; + if (c1 == '\0') + { + *invalid_reason = + xasprintf (_("In the directive number %u, there is an unterminated format directive."), + spec->directives); + FDI_SET (format, FMTDIR_ERROR); + return false; + } + c2 = format[1]; if (c2 == '<' || c2 == '>' || c2 == '=' || c2 == '^') diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am index b0d96c83e..cfbc7179c 100644 --- a/gettext-tools/tests/Makefile.am +++ b/gettext-tools/tests/Makefile.am @@ -139,7 +139,7 @@ TESTS = gettext-1 gettext-2 \ xgettext-properties-4 \ xgettext-rst-1 xgettext-rst-2 \ xgettext-python-1 xgettext-python-2 xgettext-python-3 \ - xgettext-python-4 xgettext-python-5 \ + xgettext-python-4 xgettext-python-5 xgettext-python-6 \ xgettext-python-stackovfl-1 xgettext-python-stackovfl-2 \ xgettext-python-stackovfl-3 xgettext-python-stackovfl-4 \ xgettext-ruby-1 \ diff --git a/gettext-tools/tests/xgettext-python-6 b/gettext-tools/tests/xgettext-python-6 new file mode 100755 index 000000000..72657a0bb --- /dev/null +++ b/gettext-tools/tests/xgettext-python-6 @@ -0,0 +1,23 @@ +#!/bin/sh +. "${srcdir=.}/init.sh"; path_prepend_ . ../src + +# Test Python support: a python-brace-format string with a NUL byte. + +tr X '\0' <<\EOF > xg-py-6.py +_("{0:X>}") +EOF + +: ${XGETTEXT=xgettext} +${XGETTEXT} --omit-header --no-location -d xg-py-6.tmp xg-py-6.py || Exit 1 +LC_ALL=C tr -d '\r' < xg-py-6.tmp.po > xg-py-6.po || Exit 1 + +cat <<EOF > xg-py-6.ok +msgid "{0:" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} xg-py-6.ok xg-py-6.po +result=$? + +exit $result |