Skip to content

grep: fix output duplication when number of matches is greater than MAX_MATCHES #1442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

vchimishuk
Copy link

@vchimishuk vchimishuk commented Sep 29, 2024

When color is enable grep(1) searches for MAX_MATCHES, prints the line and then repeats these steps again untill all matches in the line are found. The issue is that every time grep(1) prints the whole line which leads to the situation when line is duplicated (tripled, quadruple, ...) in stdout.

Bug can be reproduced with the next simple command. It prints line twice when only single line is expected.

$ for i in $(seq 33); do echo -n "foobar"; done | ./grep --color=auto foo


By the way, I tried to run tests in usr.bin/grep/tests folder using kyua test command on main branch and looks like tests are broken or something. Many of them fails with failed: atf-check failed; message.

…AX_MATCHES

When color is enable grep(1) searches for MAX_MATCHES, prints the line and then
repeats these steps again untill all matches in the line are found. The issue
is that every time grep(1) prints the whole line which leads to the situation
when line is duplicated (tripled, quadruple, ...) in stdout.

Bug can be reproduced with the next simple command. It prints line twice when
only single line is expected.

$ for i in $(seq 33); do echo -n "foobar"; done | ./grep --color=auto foo
@jlduran
Copy link
Member

jlduran commented Oct 3, 2024

By the way, I tried to run tests in usr.bin/grep/tests folder using kyua test command on main branch and looks like tests are broken or something. Many of them fails with failed: atf-check failed; message.

You should be able to run both test suites, the one from FreeBSD you pointed out and the one from NetBSD (contrib/netbsd-tests/usr.bin/grep).
Both get installed (usually) under /usr/tests, more specifically under usr.bin/grep.

For example:

Tested with Cirrus CI

https://github.com/jlduran/freebsd-src/tree/gh_1442
https://cirrus-ci.com/task/6524118865018880

make -C /usr/src/usr.bin/grep -j$(sysctl -n hw.ncpu) all install
kyua test -k /usr/tests/Kyuafile usr.bin/grep/
usr.bin/grep/grep_freebsd_test:gnuext  ->  passed  [0.045s]
usr.bin/grep/grep_freebsd_test:grep_r_implied  ->  passed  [0.019s]
usr.bin/grep/grep_freebsd_test:rgrep  ->  passed  [0.017s]
usr.bin/grep/grep_freebsd_test:zflag  ->  passed  [0.009s]
usr.bin/grep/grep_test:badcontext  ->  passed  [0.029s]
usr.bin/grep/grep_test:basic  ->  passed  [0.018s]
usr.bin/grep/grep_test:begin_end  ->  passed  [0.019s]
usr.bin/grep/grep_test:binary  ->  passed  [0.011s]
usr.bin/grep/grep_test:binary_flags  ->  passed  [0.044s]
usr.bin/grep/grep_test:cflag  ->  passed  [0.026s]
usr.bin/grep/grep_test:color  ->  passed  [0.033s]
usr.bin/grep/grep_test:context  ->  passed  [0.043s]
usr.bin/grep/grep_test:context2  ->  passed  [0.023s]
usr.bin/grep/grep_test:egrep  ->  passed  [0.016s]
usr.bin/grep/grep_test:egrep_empty_invalid  ->  passed  [0.014s]
usr.bin/grep/grep_test:egrep_sanity  ->  passed  [0.032s]
usr.bin/grep/grep_test:emptyfile  ->  passed  [0.015s]
usr.bin/grep/grep_test:escmap  ->  passed  [0.013s]
usr.bin/grep/grep_test:excessive_matches  ->  passed  [0.039s]
usr.bin/grep/grep_test:f_file_empty  ->  passed  [0.010s]
usr.bin/grep/grep_test:fgrep_icase  ->  passed  [0.019s]
usr.bin/grep/grep_test:fgrep_multipattern  ->  passed  [0.016s]
usr.bin/grep/grep_test:fgrep_oflag  ->  passed  [0.044s]
usr.bin/grep/grep_test:fgrep_sanity  ->  passed  [0.017s]
usr.bin/grep/grep_test:file_exp  ->  passed  [0.016s]
usr.bin/grep/grep_test:grep_nomatch_flags  ->  passed  [0.053s]
usr.bin/grep/grep_test:grep_sanity  ->  passed  [0.026s]
usr.bin/grep/grep_test:ignore_case  ->  passed  [0.013s]
usr.bin/grep/grep_test:invert  ->  passed  [0.012s]
usr.bin/grep/grep_test:matchall  ->  passed  [0.025s]
usr.bin/grep/grep_test:mflag  ->  passed  [0.021s]
usr.bin/grep/grep_test:mflag_trail_ctx  ->  passed  [0.020s]
usr.bin/grep/grep_test:mmap  ->  passed  [0.018s]
usr.bin/grep/grep_test:negative  ->  passed  [0.012s]
usr.bin/grep/grep_test:nonexistent  ->  passed  [0.010s]
usr.bin/grep/grep_test:ocolor_metadata  ->  passed  [0.034s]
usr.bin/grep/grep_test:oflag_zerolen  ->  passed  [0.047s]
usr.bin/grep/grep_test:recurse  ->  passed  [0.017s]
usr.bin/grep/grep_test:recurse_symlink  ->  passed  [0.016s]
usr.bin/grep/grep_test:wflag_emptypat  ->  passed  [0.019s]
usr.bin/grep/grep_test:whole_line  ->  passed  [0.015s]
usr.bin/grep/grep_test:word_regexps  ->  passed  [0.018s]
usr.bin/grep/grep_test:wv_combo_break  ->  passed  [0.021s]
usr.bin/grep/grep_test:xflag  ->  passed  [0.012s]
usr.bin/grep/grep_test:xflag_emptypat  ->  passed  [0.045s]
usr.bin/grep/grep_test:xflag_emptypat_plus  ->  passed  [0.020s]
usr.bin/grep/grep_test:zerolen  ->  passed  [0.017s]
usr.bin/grep/grep_test:zgrep  ->  passed  [0.019s]
usr.bin/grep/grep_test:zgrep_combined_flags  ->  expected_failure: known but unsolved zgrep wrapper script regression: atf-check failed; see the output of the test for details  [0.029s]
usr.bin/grep/grep_test:zgrep_eflag  ->  passed  [0.023s]
usr.bin/grep/grep_test:zgrep_empty_eflag  ->  passed  [0.017s]
usr.bin/grep/grep_test:zgrep_fflag  ->  passed  [0.023s]
usr.bin/grep/grep_test:zgrep_long_eflag  ->  passed  [0.015s]
usr.bin/grep/grep_test:zgrep_multiple_eflags  ->  expected_failure: known but unsolved zgrep wrapper script regression: atf-check failed; see the output of the test for details  [0.016s]
usr.bin/grep/grep_test:zgrep_multiple_files  ->  passed  [0.033s]
Results file id is usr_tests.20241003-063631-149783
Results saved to /.kyua/store/results.usr_tests.20241003-063631-149783.db
55/55 passed (0 failed)

@vchimishuk
Copy link
Author

vchimishuk commented Oct 3, 2024

You should be able to run both test suites, the one from FreeBSD you pointed out and the one from NetBSD (contrib/netbsd-tests/usr.bin/grep). Both get installed (usually) under /usr/tests, more specifically under usr.bin/grep.

For example:

I see now. So, tests are passing, that's a good news.
Thank you.

@jlduran
Copy link
Member

jlduran commented Oct 3, 2024

I've added a dumb test:

jlduran@d2a23d6

It would be good if a regression test is added as well.

Given the test is under contrib, try wrapping it with # Begin FreeBSD and # End FreeBSD comments

@jlduran
Copy link
Member

jlduran commented Oct 4, 2024

This is more or less what I have in mind:

--- /dev/null
+++ b/contrib/netbsd-tests/usr.bin/grep/d_color_d.out
@@ -0,0 +1 @@
+�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar�[01;31m�[Kfoo�[m�[Kbar
diff --git a/contrib/netbsd-tests/usr.bin/grep/t_grep.sh b/contrib/netbsd-tests/usr.bin/grep/t_grep.sh
index b1412a7a0715..7b181c1bcc54 100755
--- a/contrib/netbsd-tests/usr.bin/grep/t_grep.sh
+++ b/contrib/netbsd-tests/usr.bin/grep/t_grep.sh
@@ -426,6 +426,12 @@ color_body()
 
 	atf_check -o file:"$(atf_get_srcdir)/d_color_c.out" \
 	    grep --color=always -f grepfile "$(atf_get_srcdir)/d_color_b.in"
+	# Begin FreeBSD
+	MAX_MATCHES=32
+	for _ in $(seq $((MAX_MATCHES + 1))); do printf "%s" "foobar"; done > grepfile
+	atf_check -o file:"$(atf_get_srcdir)/d_color_d.out" \
+	    grep --color=always foo grepfile
+	# End FreeBSD
 }
 
 atf_test_case f_file_empty
diff --git a/usr.bin/grep/tests/Makefile b/usr.bin/grep/tests/Makefile
index b3c79657e53c..1db5ebea5c62 100644
--- a/usr.bin/grep/tests/Makefile
+++ b/usr.bin/grep/tests/Makefile
@@ -12,6 +12,7 @@ ${PACKAGE}FILES+=		d_color_a.out
 ${PACKAGE}FILES+=		d_color_b.in
 ${PACKAGE}FILES+=		d_color_b.out
 ${PACKAGE}FILES+=		d_color_c.out
+${PACKAGE}FILES+=		d_color_d.out
 ${PACKAGE}FILES+=		d_context2_a.out
 ${PACKAGE}FILES+=		d_context2_b.out
 ${PACKAGE}FILES+=		d_context2_c.out

@vchimishuk
Copy link
Author

Thank you, @jlduran. I'll add tests tomorrow. I want to run tests locally to make sure everything is fine.

@vchimishuk
Copy link
Author

@jlduran, I've added a commit with the testcase you proposed. Unfortunately I was not able to run tests locally, looks like I messed up with permissions or something. make buildworld takes 7 hours on my machine, so I left it for now. Hope Github CI verification will do the check.
Thank you for helping with the testcase.

Copy link
Member

@jlduran jlduran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thank you!
There should be no need to buildworld, try:

make -C /usr/src/usr.bin/grep -j$(sysctl -n hw.ncpu) all install
kyua test -k /usr/tests/Kyuafile usr.bin/grep/

@@ -426,6 +426,13 @@ color_body()

atf_check -o file:"$(atf_get_srcdir)/d_color_c.out" \
grep --color=always -f grepfile "$(atf_get_srcdir)/d_color_b.in"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This empty line is not present in the NetBSD test suite. While aesthetically pleasing, it worsens the diffs when importing the changes from NetBSD.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Fixed.

@vchimishuk
Copy link
Author

Nice, thank you! There should be no need to buildworld, try:

make -C /usr/src/usr.bin/grep -j$(sysctl -n hw.ncpu) all install
kyua test -k /usr/tests/Kyuafile usr.bin/grep/

Without buildworld /usr/tests/Kyuafile file was missing for me.

I have the next error by the way. But /usr/tests/usr.bin/grep/ folder seems to be fine and contains test files.

$ sudo kyua test -k /usr/tests/Kyuafile usr.bin/grep/
Results file id is usr_tests.20241007-122554-057863          
Results saved to /root/.kyua/store/results.usr_tests.20241007-122554-057863.db
kyua: W: No test cases matched by the filter 'usr.bin/grep'.

@jlduran
Copy link
Member

jlduran commented Oct 7, 2024

Nice, thank you! There should be no need to buildworld, try:

make -C /usr/src/usr.bin/grep -j$(sysctl -n hw.ncpu) all install
kyua test -k /usr/tests/Kyuafile usr.bin/grep/

Without buildworld /usr/tests/Kyuafile file was missing for me.

I have the next error by the way. But /usr/tests/usr.bin/grep/ folder seems to be fine and contains test files.

$ sudo kyua test -k /usr/tests/Kyuafile usr.bin/grep/
Results file id is usr_tests.20241007-122554-057863          
Results saved to /root/.kyua/store/results.usr_tests.20241007-122554-057863.db
kyua: W: No test cases matched by the filter 'usr.bin/grep'.

Try changing into the /usr/tests/usr.bin/grep directory and listing the tests:

$ cd /usr/tests/usr.bin/grep
$ kyua list
grep_freebsd_test:gnuext
grep_freebsd_test:grep_r_implied
...

There should be a Kyuafile. If there are no syntax errors with the tests, the list shows up.
You should be able to run the tests:

# kyua test

Or debug a specific test:

# kyua debug grep_test:color
Executing command [ grep --color=auto -e .* -e a /usr/tests/usr.bin/grep/d_color_a.in ]
Executing command [ grep --color=auto -f grepfile /usr/tests/usr.bin/grep/d_color_b.in ]
Executing command [ grep --color=always -f grepfile /usr/tests/usr.bin/grep/d_color_b.in ]
Executing command [ grep --color=always foo grepfile ]
grep_test:color  ->  passed

@vchimishuk
Copy link
Author

kyua works just fine when executing directly from /usr/tests/usr.bin/grep/. Thank you!
I need to learn more about this tool. Using it for the first time.

$ kyua debug grep_test:color
Executing command [ grep --color=auto -e .* -e a /usr/tests/usr.bin/grep/d_color_a.in ]
Executing command [ grep --color=auto -f grepfile /usr/tests/usr.bin/grep/d_color_b.in ]
Executing command [ grep --color=always -f grepfile /usr/tests/usr.bin/grep/d_color_b.in ]
Executing command [ grep --color=always foo grepfile ]
grep_test:color  ->  passed
$

@vchimishuk
Copy link
Author

jlduran, is there any plans to merge the patch into upstream?

@jlduran
Copy link
Member

jlduran commented Oct 20, 2024

jlduran, is there any plans to merge the patch into upstream?

I hope so, yes! This pull request fixes a bug present in BSD grep (known to be present on FreeBSD and macOS) and documents it using a test. I do not want to triage directly to a CODEOWNER, let's wait a few more days as the group in charge of triaging usually works in waves. Feel free to queue up more fixes if you plan on doing so.

Thank you for your patience™.

@markjdb
Copy link
Member

markjdb commented Nov 29, 2024

@kevans91 would you have any time to review the change?

Copy link
Contributor

@kevans91 kevans91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, one nit that we can probably just do pre-commit

@@ -223,6 +225,9 @@ procmatch_match(struct mprintc *mc, struct parsec *pc)
else
break;
}
pc->matchidx = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: let's add a blank line above this, please

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@bsdimp bsdimp added ready Seems ready to go, subject to final sanity check changes-required Cannot land as is, change requested of submitter and removed ready Seems ready to go, subject to final sanity check labels Apr 19, 2025
@bsdimp
Copy link
Member

bsdimp commented Apr 20, 2025

The conflicts that were detected were larger than I felt comfortable resolving. This needs to be rebased to proceed.

@kevans91
Copy link
Contributor

Sorry, someone had woken up a patch that I had from two or three years ago and I committed that in the interim, which seems to have been the exact problem from here that I'd forgotten about... see 4c9ffb1. We should just extract the test additions and commit those from here, I think.

@kevans91
Copy link
Contributor

Additional contet for their problem is here: https://lists.freebsd.org/archives/freebsd-current/2025-March/007176.html

@vchimishuk
Copy link
Author

The conflicts that were detected were larger than I felt comfortable resolving. This needs to be rebased to proceed.

@bsdimp, @kevans91, should I updated my PR to resolve merge conflicts or the problem is going to be fixed using different patch?

@kevans91
Copy link
Contributor

The conflicts that were detected were larger than I felt comfortable resolving. This needs to be rebased to proceed.

@bsdimp, @kevans91, should I updated my PR to resolve merge conflicts or the problem is going to be fixed using different patch?

Can you confirm your test cases against the latest main (or stable/14), please? The problem should be fixed already- if it is, then let's pare this down to your test additions and land that.

Copy link

Thank you for taking the time to contribute to FreeBSD!
There is an issue that needs to be fixed:

@vchimishuk
Copy link
Author

The conflicts that were detected were larger than I felt comfortable resolving. This needs to be rebased to proceed.

@bsdimp, @kevans91, should I updated my PR to resolve merge conflicts or the problem is going to be fixed using different patch?

Can you confirm your test cases against the latest main (or stable/14), please? The problem should be fixed already- if it is, then let's pare this down to your test additions and land that.

@kevans91, yeah, I can confirm that it has been fixed.
I've added a few commits to the current PR to remove all C code changes and left only test files changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changes-required Cannot land as is, change requested of submitter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants