Subject: Re: [bug-diffutils] bug#77840: Testing diffutils 3.12 on PPC Mac OS X 10.4.11, Tiger, produces so many failures

From: Peter Dyballa <[email-masked]>
Date: Thu, 12 Jun 2025 12:29:56 +0200

> Am 19.05.2025 um 20:16 schrieb Paul Eggert <[email-masked]>:
> 
> There are lots of failures there, but unfortunately I don't have access to that old platform so you'll need to do some more digging to isolate the cause.

I think I tracked down the cause for diff to fail on Tiger, with .gdbinit containing 'break util.c:1028':

	Starting program: /opt/local/var/macports/build/_Users_btest_ports_sysutils_diffutils/diffutils/work/diffutils-3.12/src/diff -u Portfile-graphite2-312 Portfile-graphite2
	Reading symbols for shared libraries ...................+ done
	--- Portfile-graphite2-312	2025-06-09 15:48:08.000000000 +0200
	+++ Portfile-graphite2	2025-06-09 15:48:08.000000000 +0200
	@@ -27,14 +18,23 @@
	
	Breakpoint 1, print_1_line_nl (line_flag=0x0, line=0x40137c, skip_nl=false) at util.c:1028
	(gdb) s
	output_1_line (base=0x0, limit=0x18015ef "distname", ' ' <repeats 12 times>, "${name}-${version}\nextract.suffix      .tgz\n\ncompiler.cxx_standard \\\n", ' ' <repeats 20 times>, "2011\n\nset py_ver          3.12\nset py_ver_nodot    [string map {. {}} ${py_ver}]\n\ntest.run "..., flag_format=0x0, line_flag=0x0) at util.c:1047
	(gdb) n
	(gdb) n
	(gdb) n
	(gdb) n
	(gdb) s		->  idx_t written = fwrite (base, sizeof (char), to_write, outfile);
	
	Program received signal EXC_BAD_ACCESS, Could not access memory.
	Reason: KERN_PROTECTION_FAILURE at address: 0x00000000
	0x90029b70 in memchr ()

The libc function fwrite() causes the crash. It is documented on the man page as:

     size_t
     fwrite(const void * restrict ptr, size_t size, size_t nmemb,
         FILE * restrict stream);

I tried to print out some values:
	
	(gdb) p to_write
	$1 = 1024
	(gdb) p base
	$2 = 0x0
	(gdb) p sizeof (char)
	$3 = 1
	(gdb) p outfile
	$4 = (FILE *) 0xa0001bec

The correct output should be something like this:

	@@ -31,7 +31,7 @@
	 compiler.cxx_standard \
	                     2011
	 
	-set py_ver          3.12
	+set py_ver          3.13
	 set py_ver_nodot    [string map {. {}} ${py_ver}]
	 
	 test.run            yes

Here are the last 128+ lines of util.c as reference for the line numbers given:

  998	void
  999	print_1_line_nl (char const *line_flag, char const *const *line, bool skip_nl)
 1000	{
 1001	  char const *base = line[0], *limit = line[1]; /* Help the compiler.  */
 1002	  FILE *out = outfile; /* Help the compiler some more.  */
 1003	  char const *flag_format = nullptr;
 1004	
 1005	  /* If -T was specified, use a Tab between the line-flag and the text.
 1006	     Otherwise use a Space (as Unix diff does).
 1007	     Print neither space nor tab if line-flags are empty.
 1008	     But omit trailing blanks if requested.  */
 1009	
 1010	  if (line_flag && *line_flag)
 1011	    {
 1012	      char const *flag_format_1 = flag_format = initial_tab ? "%s\t" : "%s ";
 1013	      char const *line_flag_1 = line_flag;
 1014	
 1015	      if (suppress_blank_empty && **line == '\n')
 1016	        {
 1017	          flag_format_1 = "%s";
 1018	
 1019	          /* This hack to omit trailing blanks takes advantage of the
 1020	             fact that the only way that LINE_FLAG can end in a blank
 1021	             is when LINE_FLAG consists of a single blank.  */
 1022	          line_flag_1 += *line_flag_1 == ' ';
 1023	        }
 1024	
 1025	      fprintf (out, flag_format_1, line_flag_1);
 1026	    }
 1027	
 1028	  output_1_line (base, limit - (skip_nl && limit[-1] == '\n'), flag_format, line_flag);
 1029	
 1030	  if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
 1031	    {
 1032	      set_color_context (RESET_CONTEXT);
 1033	      fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
 1034	    }
 1035	}
 1036	
 1037	/* Output a line from BASE up to LIMIT.
 1038	   With -t, expand white space characters to spaces, and if FLAG_FORMAT
 1039	   is nonzero, output it with argument LINE_FLAG after every
 1040	   internal carriage return, so that tab stops continue to line up.  */
 1041	
 1042	void
 1043	output_1_line (char const *base, char const *limit, char const *flag_format,
 1044	               char const *line_flag)
 1045	{
 1046	  enum { MAX_CHUNK = 1024 };
 1047	  if (!expand_tabs)
 1048	    {
 1049	      idx_t left = limit - base;
 1050	      while (left)
 1051	        {
 1052	          idx_t to_write = MIN (left, MAX_CHUNK);
 1053	          idx_t written = fwrite (base, sizeof (char), to_write, outfile);
 1054	          process_signals ();
 1055	          if (written < to_write)
 1056	            return;
 1057	          base += written;
 1058	          left -= written;
 1059	        }
 1060	    }
 1061	  else
 1062	    {
 1063	      FILE *out = outfile;
 1064	      char const *t = base;
 1065	      intmax_t tab = 0, column = 0, tab_size = tabsize;
 1066	      int counter_proc_signals = 0;
 1067	
 1068	      while (t < limit)
 1069	        {
 1070	          counter_proc_signals++;
 1071	          if (counter_proc_signals == MAX_CHUNK)
 1072	            {
 1073	              process_signals ();
 1074	              counter_proc_signals = 0;
 1075	            }
 1076	
 1077		  switch (*t)
 1078	            {
 1079	            case '\t':
 1080		      t++;
 1081		      do
 1082			if (putc (' ', out) < 0)
 1083			  return;
 1084		      while (++column < tab_size);
 1085	
 1086		      tab++;
 1087		      column = 0;
 1088	              break;
 1089	
 1090	            case '\r':
 1091		      t++;
 1092		      if (putc ('\r', out) < 0)
 1093			return;
 1094	              if (flag_format && t < limit && *t != '\n')
 1095			if (fprintf (out, flag_format, line_flag) < 0)
 1096			  return;
 1097	              tab = column = 0;
 1098	              break;
 1099	
 1100	            case '\b':
 1101		      t++;
 1102		      if (0 < column)
 1103			column--;
 1104		      else if (0 < tab)
 1105			{
 1106			  tab--;
 1107			  column = tab_size - 1;
 1108			}
 1109		      else
 1110			continue;
 1111		      if (putc ('\b', out) < 0)
 1112			return;
 1113	              break;
 1114	
 1115	            default:;
 1116		      mcel_t g = mcel_scan (t, limit);
 1117		      column += g.err ? 1 : c32isprint (g.ch) ? c32width (g.ch) : 0;
 1118		      tab += column / tab_size;
 1119		      column %= tab_size;
 1120		      if (fwrite (t, sizeof *t, g.len, outfile) != g.len)
 1121			return;
 1122		      t += g.len;
 1123	              break;
 1124	            }
 1125	        }
 1126	    }
 1127	}

Is there anything I can do to find more clues?

--

Greetings

  Pete

Life is the only flaw in an otherwise perfect nonexistence
				– Schopenhauer