[BUG][GNU a2ps 4.15.7] Stack-based buffer overflow (CWE-121) in list_options() via --list-options and oversized -E style name

wheat MAX <[email protected]> Tue, 2 Dec 2025 22:32:41 +0800
Newsgroups gmane.comp.printing.a2ps.bugs
Message-ID <CAKugowCmV14Xke03=G6vgGgpm4+BhsOsOFizdg8FUK6a_6oA_w@mail.gmail.com>
--0000000000008eeb190644f8f845
Content-Type: multipart/alternative; boundary="0000000000008eeb170644f8f843"

--0000000000008eeb170644f8f843
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Hello Maintainer, I would like to submit a new vulnerability report for GNU
a2ps that contains a stack-based buffer overflow (CWE-121) in the
--list-options reporting path of the main a2ps binary.

#Summary#
In GNU a2ps 4.15.7, the function list_options() in src/main.c (or the
equivalent source file) contains a stack-based buffer overflow when the
program is invoked with --list-options and a very long argument to -E
(style selection).
The option handler stores the user-controlled style string into a global
pointer style_request. Later, list_options() unconditionally copies
style_request into a fixed-size stack buffer.
Because style_request is directly derived from the -E command-line argument
and its length is never checked, a sufficiently long -E value causes
strcpy(buf, style_request) to write past the end of the 256-byte stack
buffer buf, corrupting the stack frame. With typical builds (stack
protector enabled), this results in an immediate crash due to stack
smashing detection; in builds without stack protection it yields classic
stack corruption and potential control-flow hijacking.
This corresponds to CWE-121: Stack-Based Buffer Overflow.

#Affected Versions#
Confirmed in GNU a2ps 4.15.7 released and Git master branch.

#Root Cause#
The -E option handler stores an unbounded, user-controlled string from the
command line (optional_arg of -E) into style_request via xstrdup(), without
any length check. When --list-options is used, main() calls list_options(),
whose =E2=80=9CPretty printing=E2=80=9D section copies style_request into a=
 fixed-size
stack buffer char buf[256] using strcpy(). IS_EMPTY(style_request) only
checks for NULL or empty strings and does not constrain length. Thus, a
very long -E<arg> value leads to strcpy(buf, style_request) overflowing
buf[256], causing a stack-based buffer overflow.

#Reproduction & PoC#
First, construct a very long style name (e.g., 4,000 'A' characters) in a
shell variable:
-----
hakujyo@hakujyo-virtual-machine:~/tp/bin$ LONG_STYLE=3D$(python3 -c
'print("A"*4000)')
-----
Then run a2ps with --list-options and pass this long value to -E:
-----
hakujyo@hakujyo-virtual-machine:~/tp/bin$ ./a2ps --list-options
-E"$LONG_STYLE"
-----
The program terminates with a stack-smashing error caused by list_options()=
.
'Segmentation fault (core dumped)' caused by the stack corruption in
list_options().

#Fix#
In list_options(), avoid copying the user-controlled style_request into the
fixed-size stack buffer buf using strcpy(). Instead, select the string to
print via a pointer and pass it directly to fprintf().
This ensures that the style name-regardless of length-is no longer copied
into a 256-byte stack buffer, preventing the stack-based buffer overflow
while preserving the existing behavior of --list-options.

#Attachments#
PoC command: a2ps_stack_overflow_poc.txt
Suggested patch: a2ps_stack_overflow_fix.patch (derived from the change
above).

Thanks.

Best regards,
[email protected]

--0000000000008eeb170644f8f843
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">Hello Maintainer, I would like to submit a new vulnerabili=
ty report for GNU a2ps that contains a stack-based buffer overflow (CWE-121=
) in the --list-options reporting path of the main a2ps binary.<br><br>#Sum=
mary#<br>In GNU a2ps 4.15.7, the function list_options() in src/main.c (or =
the equivalent source file) contains a stack-based buffer overflow when the=
 program is invoked with --list-options and a very long argument to -E (sty=
le selection).<br>The option handler stores the user-controlled style strin=
g into a global pointer style_request. Later, list_options() unconditionall=
y copies style_request into a fixed-size stack buffer.<br>Because style_req=
uest is directly derived from the -E command-line argument and its length i=
s never checked, a sufficiently long -E value causes strcpy(buf, style_requ=
est) to write past the end of the 256-byte stack buffer buf, corrupting the=
 stack frame. With typical builds (stack protector enabled), this results i=
n an immediate crash due to stack smashing detection; in builds without sta=
ck protection it yields classic stack corruption and potential control-flow=
 hijacking.<br>This corresponds to CWE-121: Stack-Based Buffer Overflow.<br=
><br>#Affected Versions#<br>Confirmed in GNU a2ps 4.15.7 released and Git m=
aster branch.<br><br>#Root Cause#<br>The -E option handler stores an unboun=
ded, user-controlled string from the command line (optional_arg of -E) into=
 style_request via xstrdup(), without any length check. When --list-options=
 is used, main() calls list_options(), whose =E2=80=9CPretty printing=E2=80=
=9D section copies style_request into a fixed-size stack buffer char buf[25=
6] using strcpy(). IS_EMPTY(style_request) only checks for NULL or empty st=
rings and does not constrain length. Thus, a very long -E&lt;arg&gt; value =
leads to strcpy(buf, style_request) overflowing buf[256], causing a stack-b=
ased buffer overflow.<br><br>#Reproduction &amp; PoC#<br>First, construct a=
 very long style name (e.g., 4,000 &#39;A&#39; characters) in a shell varia=
ble:<br>-----<br>hakujyo@hakujyo-virtual-machine:~/tp/bin$ LONG_STYLE=3D$(p=
ython3 -c &#39;print(&quot;A&quot;*4000)&#39;)<br>-----<br>Then run a2ps wi=
th --list-options and pass this long value to -E:<br>-----<br>hakujyo@hakuj=
yo-virtual-machine:~/tp/bin$ ./a2ps --list-options -E&quot;$LONG_STYLE&quot=
;<br>-----<br>The program terminates with a stack-smashing error caused by =
list_options().<br>&#39;Segmentation fault (core dumped)&#39; caused by the=
 stack corruption in list_options().<br><br>#Fix#<br>In list_options(), avo=
id copying the user-controlled style_request into the fixed-size stack buff=
er buf using strcpy(). Instead, select the string to print via a pointer an=
d pass it directly to fprintf().<br>This ensures that the style name-regard=
less of length-is no longer copied into a 256-byte stack buffer, preventing=
 the stack-based buffer overflow while preserving the existing behavior of =
--list-options.<br><br>#Attachments#<br>PoC command: a2ps_stack_overflow_po=
c.txt<br>Suggested patch: a2ps_stack_overflow_fix.patch (derived from the c=
hange above).<br><br>Thanks.<br><br>Best regards,<br><a href=3D"mailto:maxt=
[email protected]" target=3D"_blank">[email protected]</a></div>

--0000000000008eeb170644f8f843--

--0000000000008eeb190644f8f845
Content-Type: text/plain; charset="US-ASCII"; name="a2ps_stack_overflow_poc.txt"
Content-Disposition: attachment; filename="a2ps_stack_overflow_poc.txt"
Content-Transfer-Encoding: base64
Content-ID: <f_miooergf0>
X-Attachment-Id: f_miooergf0

aGFrdWp5b0BoYWt1anlvLXZpcnR1YWwtbWFjaGluZTp+L3RwL2JpbiQgTE9OR19TVFlMRT0kKHB5
dGhvbjMgLWMgJ3ByaW50KCJBIio0MDAwKScpDQpoYWt1anlvQGhha3VqeW8tdmlydHVhbC1tYWNo
aW5lOn4vdHAvYmluJCAuL2EycHMgLS1saXN0LW9wdGlvbnMgLUUiJExPTkdfU1RZTEUiDQogICAg
ICAgICAgICAgICAgICAgICAgQ29uZmlndXJhdGlvbiBzdGF0dXMgb2YgYTJwcyA0LjE1LjcNCiAg
ICAgICAgICAgICAgICAgICAgICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0K
DQpTaGVldHM6DQotLS0tLS0tDQogIG1lZGl1bSAgICAgICAgICA9IGE0LCBsYW5kc2NhcGUNCiAg
cGFnZSBsYXlvdXQgICAgID0gMiB4IDEsIHJvd3MgZmlyc3QNCiAgYm9yZGVycyAgICAgICAgID0g
eWVzDQogIGZpbGUgYWxpZ25tZW50ICA9IHBhZ2UNCiAgaW50ZXJpb3IgbWFyZ2luID0gMA0KDQpW
aXJ0dWFsIHBhZ2VzOg0KLS0tLS0tLS0tLS0tLS0NCiAgbnVtYmVyIGxpbmVzICAgICAgICAgPSBu
bw0KICBmb3JtYXQgICAgICAgICAgICAgICA9IDgwIGNoYXJhY3RlcnMgcGVyIGxpbmUNCiAgdGFi
dWxhdGlvbiBzaXplICAgICAgPSA4DQogIG5vbiBwcmludGFibGUgZm9ybWF0ID0gY2FyZXQgKGku
ZS4sIGBeQycsIGBNLV5DJyBldGMuKQ0KDQpIZWFkZXJzOg0KLS0tLS0tLS0NCiAgaGVhZGVyICAg
ICAgID0gJWENCiAgbGVmdCBmb290ZXIgID0gIz9sISVFISM/dnwlRXwlcy4vJXMjfCENCiAgZm9v
dGVyICAgICAgID0gIz9sfCMhcy0kZi0sIC18fA0KICByaWdodCBmb290ZXIgPSAjP2whJXMuLyVz
IyEjP3Z8JXMuLyVzI3wlRXwhDQogIGxlZnQgdGl0bGUgICA9ICM/Mnx8JGUgJFR8DQogIGNlbnRl
ciB0aXRsZSA9ICM/MXwkdDF8JG58DQogIHJpZ2h0IHRpdGxlICA9ICM/MnwkdDJ8JFF8DQogIHVu
ZGVyIGxheSAgICA9IA0KDQpJbnB1dDoNCi0tLS0tLQ0KICB0cnVuY2F0ZSBsaW5lcyA9IG5vDQog
IGludGVycHJldCAgICAgID0geWVzDQogIGVuZCBvZiBsaW5lICAgID0gYW55IHR5cGUNCiAgZW5j
b2RpbmcgICAgICAgPSBJU08tODg1OS0xDQogIGRvY3VtZW50IHRpdGxlID0gIzEwIWZ8JG58LCB8
DQogIHByb2xvZ3VlICAgICAgID0gYncNCiAgcHJpbnQgYW55d2F5ICAgPSBubw0KICBkZWxlZ2F0
aW5nICAgICA9IHllcw0KDQpTZWdtZW50YXRpb24gZmF1bHQgKGNvcmUgZHVtcGVkKQ0K
--0000000000008eeb190644f8f845
Content-Type: application/octet-stream; name="a2ps_stack_overflow_fix.patch"
Content-Disposition: attachment; filename="a2ps_stack_overflow_fix.patch"
Content-Transfer-Encoding: base64
Content-ID: <f_mioof3cb1>
X-Attachment-Id: f_mioof3cb1

ZGlmZiAtLWdpdCBhLy4vc3JjL21haW4uYyBiLy4vc3JjL21haW5fcGF0Y2guYwppbmRleCBlY2Nk
MmZkLi4yYjVjZWVjIDEwMDY0NAotLS0gYS8uL3NyYy9tYWluLmMKKysrIGIvLi9zcmMvbWFpbl9w
YXRjaC5jCkBAIC0zMjEsNiArMzIxLDcgQEAgbGlzdF9vcHRpb25zIChzdHJ1Y3QgYTJwc19qb2Ig
KmFfam9iLCBGSUxFICpzdHJlYW0pCiAjZGVmaW5lIGJvb2xfdG9fc3RyaW5nKGJvb2wpICgoYm9v
bCkgPyBfKCJ5ZXMiKSA6IF8oIm5vIikpCiAgIGNoYXIgYnVmWzI1Nl0sIGJ1ZjJbMjU2XTsKICAg
Y29uc3QgY2hhciAqY3AgPSBOVUxMLCAqY3AyID0gTlVMTDsKKyAgY29uc3QgY2hhciAqc3R5bGVf
bmFtZSA9IE5VTEw7CiAKICAgLyogVGl0bGUgb2YgLS1saXN0LW9wdGlvbnMgKCVzJXMgaXMgYGEy
cHMnIGB2ZXJzaW9uJyAqLwogICB0aXRsZSAoc3RyZWFtLCAnPScsIHRydWUsCkBAIC00MjEsMTkg
KzQyMiwyMyBAQCBsaXN0X29wdGlvbnMgKHN0cnVjdCBhMnBzX2pvYiAqYV9qb2IsIEZJTEUgKnN0
cmVhbSkKIAogICAvKgogICAgKiBQcmV0dHkgcHJpbnRpbmcKKyAgICoKKyAgICogTm8gc3RyY3B5
CisgICAqCiAgICAqLwogICBpZiAoSVNfRU1QVFkgKHN0eWxlX3JlcXVlc3QpKQogICAgIC8qIFRS
QU5TOiBhMnBzIC1FIC0tbGlzdD1vcHRpb25zLiAgV2FybmluZywgdGhpcyBhbnN3ZXIgaXMgYWxz
bwogICAgICAgIHVzZWQgZm9yIHRoZSBQUEQgZmlsZS4gIE1ha2UgaXQgY29tcGF0aWJsZSB3aXRo
IGJvdGguICAqLwotICAgIHN0cmNweSAoYnVmLCBfKCJzZWxlY3RlZCBhdXRvbWF0aWNhbGx5Iikp
OworICAgIHN0eWxlX25hbWUgPSBfKCJzZWxlY3RlZCBhdXRvbWF0aWNhbGx5Iik7CiAgIGVsc2UK
LSAgICBzdHJjcHkgKGJ1Ziwgc3R5bGVfcmVxdWVzdCk7CisgICAgc3R5bGVfbmFtZSA9IHN0eWxl
X3JlcXVlc3Q7CisKICAgdGl0bGUgKHN0cmVhbSwgJy0nLCBmYWxzZSwgXygiUHJldHR5LXByaW50
aW5nOlxuIikpOwogICBmcHJpbnRmIChzdHJlYW0sIF8oIlwKICAgc3R5bGUgc2hlZXQgICAgID0g
JXNcblwKICAgaGlnaGxpZ2h0IGxldmVsID0gJXNcblwKICAgc3RyaXAgbGV2ZWwgICAgID0gJWRc
biIpLAotCSAgIGJ1ZiwKKwkgICBzdHlsZV9uYW1lLAogCSAgIGhpZ2hsaWdodF9sZXZlbF90b19z
dHJpbmcgKGhpZ2hsaWdodF9sZXZlbCksCiAJICAgc3RyaXBfbGV2ZWwpOwogICBwdXRjICgnXG4n
LCBzdHJlYW0pOwo=
--0000000000008eeb190644f8f845--