Skip to content

Commit 53080e2

Browse files
committed
stdlib: Reject negative precision in io_lib:format
A negative precision passed via ~.* (e.g. io_lib:format("~.*c", [-1, $a])) would cause infinite recursion in chars/2 since -1 bsr 1 = -1. Other controls (~.*f, ~.*s, etc.) would crash with function_clause. Fix by validating precision >= 0 in precision/2, raising badarg consistently for all controls. This matches C printf semantics where negative precision via * is defined behavior (treated as omitted), but Erlang chooses to reject it explicitly.
1 parent abe54f6 commit 53080e2

2 files changed

Lines changed: 25 additions & 3 deletions

File tree

lib/stdlib/src/io_lib_format.erl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,12 @@ field_width(F, Fmt, Args) when F >= 0 ->
262262
{F,right,Fmt,Args}.
263263

264264
precision([$.|Fmt], Args) ->
265-
field_value(Fmt, Args);
265+
case field_value(Fmt, Args) of
266+
{P, _, _} when is_integer(P), P < 0 ->
267+
error(badarg);
268+
Result ->
269+
Result
270+
end;
266271
precision(Fmt, Args) ->
267272
{none,Fmt,Args}.
268273

lib/stdlib/test/io_SUITE.erl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
native_records/1, cover_fread/1,
4141
format_w_empty_map/1, format_w_limited/1,
4242
write_record_maps_order/1, write_record_latin1_encoding/1,
43-
indentation_tab/1]).
43+
indentation_tab/1, badarg/1]).
4444

4545
-export([pretty/2, trf/3, rfd/2]).
4646

@@ -80,7 +80,7 @@ all() ->
8080
native_records,
8181
format_w_empty_map, format_w_limited,
8282
write_record_maps_order, write_record_latin1_encoding,
83-
indentation_tab,
83+
indentation_tab, badarg,
8484
cover_fread].
8585

8686
%% Error cases for output.
@@ -3544,6 +3544,23 @@ indentation_tab(_Config) ->
35443544
10 = io_lib_format:indentation("ab\ncd\tef", 0),
35453545
ok.
35463546

3547+
%% Test that negative precision via * raises badarg.
3548+
badarg(_Config) ->
3549+
bad_io_lib_format("~.*c", [-1, $a]),
3550+
bad_io_lib_format("~.*s", [-1, "hello"]),
3551+
bad_io_lib_format("~.*f", [-1, 1.5]),
3552+
bad_io_lib_format("~.*e", [-1, 1.5]),
3553+
bad_io_lib_format("~.*g", [-1, 1.5]),
3554+
bad_io_lib_format("~.*b", [-1, 42]),
3555+
bad_io_lib_format("~.*B", [-1, 42]),
3556+
bad_io_lib_format("~.*x", [-1, 42, "0x"]),
3557+
bad_io_lib_format("~.*X", [-1, 42, "0x"]),
3558+
bad_io_lib_format("~.*+", [-1, 42]),
3559+
bad_io_lib_format("~.*#", [-1, 42]),
3560+
bad_io_lib_format("~.*w", [-1, foo]),
3561+
bad_io_lib_format("~.*p", [-1, foo]),
3562+
ok.
3563+
35473564
fread_bad(Format, String) ->
35483565
{error,{fread,Hint}} = io_lib:fread(Format, String),
35493566
Hint.

0 commit comments

Comments
 (0)