From 2aa8a3502fd049a3ce13164aa8b289daf2940e04 Mon Sep 17 00:00:00 2001 From: Sudhakar Verma Date: Fri, 12 Jan 2024 16:08:47 +0530 Subject: [PATCH 1/3] printf : no infinite loop --- src/uu/printf/src/printf.rs | 10 +++++++++- tests/by-util/test_printf.rs | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/uu/printf/src/printf.rs b/src/uu/printf/src/printf.rs index ad42e3894..64a3a35a7 100644 --- a/src/uu/printf/src/printf.rs +++ b/src/uu/printf/src/printf.rs @@ -11,7 +11,7 @@ use std::ops::ControlFlow; use clap::{crate_version, Arg, ArgAction, Command}; use uucore::error::{UResult, UUsageError}; -use uucore::format::{parse_spec_and_escape, FormatArgument}; +use uucore::format::{parse_spec_and_escape, FormatArgument, FormatItem}; use uucore::{format_usage, help_about, help_section, help_usage}; const VERSION: &str = "version"; @@ -46,6 +46,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; } + // See #5815 - We don't need to iter on args if no format string seen + let format_seen = parse_spec_and_escape(format_string.as_ref()) + .into_iter() + .any(|r| matches!(r, Ok(FormatItem::Spec(_)))); + if !format_seen { + return Ok(()); + } + while args.peek().is_some() { for item in parse_spec_and_escape(format_string.as_ref()) { match item?.write(stdout(), &mut args)? { diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index 4f2e1dc10..411285a0c 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -649,3 +649,8 @@ fn partial_char() { fn char_as_byte() { new_ucmd!().args(&["%c", "🙃"]).succeeds().stdout_only("ð"); } + +#[test] +fn no_infinite_loop() { + new_ucmd!().args(&["a", "b"]).succeeds().stdout_only("a"); +} From 4e5a65ee41372ca9bd35501e251b1b7bf799665e Mon Sep 17 00:00:00 2001 From: Sudhakar Verma Date: Fri, 12 Jan 2024 16:23:38 +0530 Subject: [PATCH 2/3] printf: fix clippy warnings --- src/uu/printf/src/printf.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/uu/printf/src/printf.rs b/src/uu/printf/src/printf.rs index 64a3a35a7..b0988f7b4 100644 --- a/src/uu/printf/src/printf.rs +++ b/src/uu/printf/src/printf.rs @@ -47,9 +47,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } // See #5815 - We don't need to iter on args if no format string seen - let format_seen = parse_spec_and_escape(format_string.as_ref()) - .into_iter() - .any(|r| matches!(r, Ok(FormatItem::Spec(_)))); + let format_seen = + parse_spec_and_escape(format_string.as_ref()).any(|r| matches!(r, Ok(FormatItem::Spec(_)))); if !format_seen { return Ok(()); } From cd4568f5d9862ab28dda351c7692c0bd95dcdda7 Mon Sep 17 00:00:00 2001 From: Sudhakar Verma Date: Fri, 12 Jan 2024 17:12:30 +0530 Subject: [PATCH 3/3] printf: simplify loop --- src/uu/printf/src/printf.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/uu/printf/src/printf.rs b/src/uu/printf/src/printf.rs index b0988f7b4..c98bb59a1 100644 --- a/src/uu/printf/src/printf.rs +++ b/src/uu/printf/src/printf.rs @@ -38,17 +38,20 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { None => vec![], }; + let mut format_seen = false; let mut args = values.iter().peekable(); for item in parse_spec_and_escape(format_string.as_ref()) { + if let Ok(FormatItem::Spec(_)) = item { + format_seen = true; + } match item?.write(stdout(), &mut args)? { ControlFlow::Continue(()) => {} ControlFlow::Break(()) => return Ok(()), }; } - // See #5815 - We don't need to iter on args if no format string seen - let format_seen = - parse_spec_and_escape(format_string.as_ref()).any(|r| matches!(r, Ok(FormatItem::Spec(_)))); + // Without format specs in the string, the iter would not consume any args, + // leading to an infinite loop. Thus, we exit early. if !format_seen { return Ok(()); }