Subject: dd(1) improvements, as(1) NULL dereference, adb(1) no memory message, printf(3) return value incorrect (#492) Index: src/bin/dd.c, src/bin/as/as0.s, src/bin/adb/sym.c, src/man/man1/{dd.1,as.1}, src/man/man3/printf.3 Description: 1) It would be nice if dd(1) would support bigger bufs and counts, faster skip/seek, truncating at the right place or no truncating at all, and of course printing of transfer statistics. 2) as(1) dereferences the last pointer in argv[], which is NULL. This doesn't cause as(1) to crash because 0 (NULL) on a PDP-11 is always mapped (a usable address). 3) When adb(1) is used on a stripped binary (no symbols table) it prints "no memory for symbols" which isn't really true. Additionally the debug messages of adb (enabled by rebuilding with -DDEBUG) could be a bit more useful, like print the user pc with %o instead of %d 4) When printf.3 was updated in #491 the first function (printf) return value was left at 'char *' instead of being changed to 'int' Repeat-By: 1) Use dd(1) to copy data between large raw disks and/or files. You'll quickly run into plenty of unnecessary limitations. 2) Hans: "I don't really remember how I found this. I don't think I read as0.s just for fun and spotted it. I'm sure it was because it caused some weird issue somewhere in the build, which I just don't remember anymore. Anyway, it can be seen right there, in _main: / PDP-11 assembler _main: jsr r5,csv mov $1,-(sp) / signal(SIGINT, SIG_IGN) mov $2,-(sp) jsr pc,_signal cmp (sp)+,(sp)+ ror r0 bcs 1f mov $aexit,-(sp) / signal(SIGINT, aexit) mov $2,-(sp) jsr pc,_signal cmp (sp)+,(sp)+ 1: mov 4(r5),r0 / argc mov 6(r5),curarg / argv 9: dec r0 / argc-- add $2,curarg / argv++ 1: mov *curarg,r1 ======> cmpb (r1)+,$'- <====== NULL pointer dereference if r0==1 bne 1f cmpb (r1),$'- / is this "--"? bne 8f / no - br tstb 1(r1) / check for null terminator beq 1f / got it, the "--" means read 'stdin' 'as' can read past the end of argv[] in the instruction pointed to above and try to parse argv[]'s NULL terminator as an additional argument. In a split I/D executable ('as' is built split) the global ___progname is at address 0 in D space." IF you've ever had 'as' exit with a weird status during a build of the kernel "this fix is for you" ;) Seems to be triggered when 'as' is being run from a Makefile and reading from a pipeline rather than having an explicit input file because running the failed command manually would succeed. 3) adb /bin/ls adb: no memory for symbols adb> 4) Observation. Fix: All changes submitted by Hans Rosenfeld Minor editing/reformatting by bugs@2bsd.com ;) 1) - change the "files", "ibs", "obs", "bs", and "cbs" parameters to take unsigned int values, but limit them to MAXBUF (instead of 2GB). - change the internal byte counts ibc, obc, and cbc to unsigned - change the "count" parameter to take unsigned long values - change the "skip" and "seek" parameters to take off_t values, but limit them based on ibs/obs to not overflow the off_t range - implement the "skip" parameter in terms of a single lseek() call, falling back to a loop of ibs-sized read() calls if that isn't supported on the input file - implement the "seek" parameter in terms of a single lseek() call instead of a loop of obs-sized lseek() calls - truncate the output file at the position specified by "seek" - add a "notrunc" conversion flag to disable output truncation - print transfer statistics after completion of transfer - support a "status" parameter compatible with at least illumos and FreeBSD, taking three possible values: - noxfer: don't print transfer statistics - none: don't print any informational messages - progress: print transfer statistics once per second - support a "progress=n" parameter compatible with NetBSD, which if set to a non-zero value prints a "." for every n full or partial blocks transferred 2) A trivial fix for the NULL pointer derefence is to check the value of r0 after decrementing. 3) A few checks for 0 symbols need to be added to sym.c and debug statements changed in runpcs.c 4) Change "char *printf" to "int printf" Cut where indicated and save to a file (/tmp/492.patch). Then: cd / patch -p0 < /tmp/492.patch cd /usr/src/bin make dd install -s -m 751 -g staff dd /bin cd as make install make clean cd ../adb make install make clean cd /usr/src/man/man1 make dd.0 as.0 install -o bin -g bin -m 444 as.0 dd.0 /usr/man/cat1 cd ../man3 make printf.0 install -o bin -g bin -m 444 printf.0 /usr/man/cat3 This and previous updates to 2.11BSD are available at the following locations: ftp://ftp.dfupdate.se/pub/pdp11/2.11BSD https://www.tuhs.org/Archive/Distributions/UCB/2.11BSD/Patches/ ftp://ftp.2bsd.com/2.11BSD http://www.2bsd.com/2.11BSD ---------------------------cut here-------------------- *** ./usr/src/bin/dd.c.old Sun Mar 31 02:23:49 1991 --- ./usr/src/bin/dd.c Mon Aug 11 21:23:06 2025 *************** *** 1,40 **** ! #ifndef lint ! static char *sccsid = "@(#)dd.c 4.4 (Berkeley) 1/22/85"; #endif #include #include #define BIG 2147483647 #define LCASE 01 #define UCASE 02 #define SWAB 04 ! #define NERR 010 ! #define SYNC 020 int cflag; int fflag; ! int skip; ! int seekn; ! int count; ! int files = 1; char *string; char *ifile; char *ofile; char *ibuf; char *obuf; ! char *sbrk(); ! int ibs = 512; ! int obs = 512; ! int bs; ! int cbs; ! int ibc; ! int obc; ! int cbc; ! int nifr; ! int nipr; ! int nofr; ! int nopr; ! int ntrunc; int ibf; int obf; char *op; --- 1,55 ---- ! #if !defined(lint) && defined(DOSCCS) ! static char *sccsid = "@(#)dd.c 5.0 (2.11BSD) 2025/8/11"; #endif + #include + #include #include #include + #include + #define MAXBUF (54*1024) + #define SMALL 65535 #define BIG 2147483647 + + #define SNONE 1 + #define SNOXFR 2 + #define SPROGR 3 + int sflag; + #define LCASE 01 #define UCASE 02 #define SWAB 04 ! #define NERR 010 ! #define SYNC 020 ! #define NTRUNC 040 int cflag; int fflag; ! char *string; char *ifile; char *ofile; char *ibuf; char *obuf; ! ! off_t skip; ! off_t seekn; ! unsigned long count; ! unsigned int files = 1; ! unsigned int ibs = 512; ! unsigned int obs = 512; ! unsigned int bs; ! unsigned int cbs; ! unsigned int ibc; ! unsigned int obc; ! unsigned int cbc; ! unsigned int progress; ! unsigned long nifr; ! unsigned long nipr; ! unsigned long nofr; ! unsigned long nopr; ! unsigned long ntrunc; ! unsigned long nbytes; int ibf; int obf; char *op; *************** *** 142,147 **** --- 157,163 ---- 0334,0335,0336,0337,0352,0353,0354,0355, 0356,0357,0372,0373,0374,0375,0376,0377, }; + struct timeval tstart; main(argc, argv) *************** *** 151,157 **** int (*conv)(); register char *ip; register c; ! int ebcdic(), ibm(), ascii(), null(), cnull(), term(), block(), unblock(); int a; conv = null; --- 167,175 ---- int (*conv)(); register char *ip; register c; ! int ebcdic(), ibm(), ascii(), null(), cnull(), term(), stats(); ! int block(), unblock(); ! long number(); int a; conv = null; *************** *** 158,176 **** for(c=1; c= MAXBUF) { ! fprintf(stderr, ! "combined buffer sizes of %lu too large, max %lu\n", ! (long)ibs + obs, MAXBUF-1); ! exit(1); ! } ! obuf = sbrk(obs); + } sbrk(64); /* For good measure */ if(ibuf == (char *)-1 || obuf == (char *)-1) { fprintf(stderr, "not enough memory\n"); *************** *** 291,314 **** if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, term); ! while(skip) { ! read(ibf, ibuf, ibs); ! skip--; } ! while(seekn) { ! lseek(obf, (long)obs, 1); ! seekn--; } loop: if(ibc-- == 0) { ibc = 0; if(count==0 || nifr+nipr!=count) { if(cflag&(NERR|SYNC)) ! for(ip=ibuf+ibs; ip>ibuf;) ! *--ip = 0; ibc = read(ibf, ibuf, ibs); } if(ibc == -1) { perror("read"); if((cflag&NERR) == 0) { --- 340,403 ---- if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, term); ! ! if (skip) { ! if (skip >= BIG/ibs) { ! fprintf(stderr, "dd: argument %D out of range\n", skip); ! exit(1); ! } ! ! if (lseek(ibf, (long)0, L_INCR) < 0) { ! while(skip) { ! read(ibf, ibuf, ibs); ! skip--; ! } ! } else { ! lseek(ibf, (off_t)skip * ibs, L_INCR); ! skip = 0; ! } } ! if (seekn) { ! if (seekn >= BIG/obs) { ! fprintf(stderr, "dd: argument %D out of range\n", seekn); ! exit(1); ! } ! ! lseek(obf, (off_t)seekn * obs, L_INCR); } + if ((cflag & NTRUNC) == 0) + ftruncate(obf, (off_t)seekn * obs); + + seekn = 0; + + if (sflag == SPROGR) { + struct itimerval itv; + + itv.it_interval.tv_sec = 1; + itv.it_interval.tv_usec = 0; + itv.it_value = itv.it_interval; + + if (signal(SIGALRM, SIG_IGN) != SIG_IGN) + signal(SIGALRM, stats); + + setitimer(ITIMER_REAL, &itv, NULL); + } + + gettimeofday(&tstart); loop: if(ibc-- == 0) { ibc = 0; if(count==0 || nifr+nipr!=count) { if(cflag&(NERR|SYNC)) ! for(ip=ibuf+ibs; ip>ibuf;) ! *--ip = 0; ibc = read(ibf, ibuf, ibs); } + if(ibc == 0 && --files<=0) { + flsh(); + term(0); + } if(ibc == -1) { perror("read"); if((cflag&NERR) == 0) { *************** *** 319,330 **** for(c=0; c 0 && ((nofr + nopr) % progress) == 0) + fprintf(stderr, "."); + nbytes += c; if(c != obc) { perror("write"); term(1); *************** *** 389,395 **** --- 478,486 ---- return(1); } + long number(big) + unsigned long big; { register char *cs; long n; *************** *** 602,610 **** stats() { ! fprintf(stderr,"%u+%u records in\n", nifr, nipr); ! fprintf(stderr,"%u+%u records out\n", nofr, nopr); ! if(ntrunc) ! fprintf(stderr,"%u truncated records\n", ntrunc); } --- 693,729 ---- stats() { + char buf[100]; + struct timeval tend; + long s, us, cs; + int len; ! if (sflag == SNONE) ! return; ! ! gettimeofday(&tend, NULL); ! ! len = sprintf(buf, "%lu+%lu records in\n%lu+%lu records out\n", ! nifr, nipr, nofr, nopr); ! write(STDERR_FILENO, buf, len); ! if(ntrunc) { ! len = sprintf(buf, "%lu truncated records\n", ntrunc); ! write(STDERR_FILENO, buf, len); ! } ! ! if (sflag == SNOXFR || nbytes == 0) ! return; ! ! s = tend.tv_sec - tstart.tv_sec; ! us = tend.tv_usec - tstart.tv_usec; ! cs = s * 100 + us / 10000; ! ! if (cs == 0) ! return; ! ! len = sprintf(buf, ! "%lu bytes transferred in %ld.%02ld secs (%lu bytes/sec)\n", ! nbytes, cs / 100, cs % 100, ! (long)(((double)nbytes*(double)100 / (double)cs))); ! write(STDERR_FILENO, buf, len); } *** ./usr/src/bin/as/as0.s.old Fri Apr 21 22:25:05 2000 --- ./usr/src/bin/as/as0.s Mon Aug 11 20:04:05 2025 *************** *** 74,82 **** mov 4(r5),r0 / argc mov 6(r5),curarg / argv 9: - dec r0 / argc-- add $2,curarg / argv++ ! 1: mov *curarg,r1 cmpb (r1)+,$'- bne 1f --- 74,82 ---- mov 4(r5),r0 / argc mov 6(r5),curarg / argv 9: add $2,curarg / argv++ ! dec r0 / argc-- ! beq 1f / argc == 0? mov *curarg,r1 cmpb (r1)+,$'- bne 1f *************** *** 85,108 **** tstb 1(r1) / check for null terminator beq 1f / got it, the "--" means read 'stdin' 8: - add $2,curarg / argv++ - dec r0 / argc-- cmpb (r1),$'u beq 3f cmpb (r1), $'V bne 2f inc overlaid ! br 1b 2: tstb (r1) bne 2f 3: mov $40,defund ! br 1b 2: cmpb (r1),$'o bne 1f mov *curarg,a.outp br 9b 1: --- 85,108 ---- tstb 1(r1) / check for null terminator beq 1f / got it, the "--" means read 'stdin' 8: cmpb (r1),$'u beq 3f cmpb (r1), $'V bne 2f inc overlaid ! br 9b 2: tstb (r1) bne 2f 3: mov $40,defund ! br 9b 2: cmpb (r1),$'o bne 1f + add $2,curarg / argv++ mov *curarg,a.outp + dec r0 / argc-- br 9b 1: *** ./usr/src/bin/adb/sym.c.old Wed Mar 19 06:16:02 2025 --- ./usr/src/bin/adb/sym.c Tue Aug 12 07:12:04 2025 *************** *** 174,179 **** --- 174,182 ---- symnum = ex->a_syms / sizeof (sym); + if (symnum == 0) + goto out; + fseek(fp, symoff, L_SET); nused = 0; for (i = 0; i < symnum; i++) *************** *** 186,191 **** --- 189,197 ---- else nused++; } + if (nused == 0) + goto out; + fseek(fp, symoff, L_SET); symtab = (struct SYMbol *)malloc(nused * sizeof (struct SYMbol)); *************** *** 204,209 **** --- 210,219 ---- continue; nused++; } + + if (nused == 0) + goto out; + symtab = (struct SYMbol *)malloc(nused * sizeof(struct SYMbol)); if (!symtab) { *************** *** 229,236 **** sp->soff = shorten(sym.n_un.n_strx); sp++; } symnum = nused; ! #ifdef debug printf("%d symbols loaded\n", nused); #endif if (globals_only) --- 239,247 ---- sp->soff = shorten(sym.n_un.n_strx); sp++; } + out: symnum = nused; ! #ifdef DEBUG printf("%d symbols loaded\n", nused); #endif if (globals_only) *** ./usr/src/bin/adb/runpcs.c.old Mon Mar 24 16:16:23 2025 --- ./usr/src/bin/adb/runpcs.c Tue Aug 12 07:12:04 2025 *************** *** 50,56 **** WHILE (loopcnt--)>0 DO #ifdef DEBUG ! printf("\ncontinue %d %d\n",userpc,execsig); #endif /* If we're at a breakpoint, execute that using execbkpt */ if (bkpt=scanbkpt(userpc == 1 ? uar0[PC] : userpc)) { --- 50,56 ---- WHILE (loopcnt--)>0 DO #ifdef DEBUG ! printf("\ncontinue %o %d\n",userpc,execsig); #endif /* If we're at a breakpoint, execute that using execbkpt */ if (bkpt=scanbkpt(userpc == 1 ? uar0[PC] : userpc)) { *************** *** 78,84 **** THEN /*stopped at bkpt*/ userpc=uar0[PC]=bkpt->loc; IF bkpt->flag==BKPTEXEC ! ORF ((bkpt->flag=BKPTEXEC, command(bkpt->comm,':')) ANDF --bkpt->count) THEN execbkpt(bkpt); execsig=0; loopcnt++; userpc=1; ELSE bkpt->count=bkpt->initcnt; --- 78,85 ---- THEN /*stopped at bkpt*/ userpc=uar0[PC]=bkpt->loc; IF bkpt->flag==BKPTEXEC ! ORF ((bkpt->flag=BKPTEXEC, command(bkpt->comm,':')) ! ANDF --bkpt->count) THEN execbkpt(bkpt); execsig=0; loopcnt++; userpc=1; ELSE bkpt->count=bkpt->initcnt; *************** *** 128,134 **** { int bkptloc; #ifdef DEBUG ! printf("exbkpt: %d\n",bkptr->count); #endif bkptloc = bkptr->loc; ptrace(PT_WRITE_I,pid,bkptloc,bkptr->ins); --- 129,135 ---- { int bkptloc; #ifdef DEBUG ! printf("execbkpt: %o %d\n",bkptr->loc,bkptr->count); #endif bkptloc = bkptr->loc; ptrace(PT_WRITE_I,pid,bkptloc,bkptr->ins); *** ./usr/src/man/man1/dd.1.old Sun Dec 14 17:06:08 1986 --- ./usr/src/man/man1/dd.1 Mon Aug 11 16:47:27 2025 *************** *** 4,10 **** .\" .\" @(#)dd.1 6.1 (Berkeley) 4/29/85 .\" ! .TH DD 1 "April 29, 1985" .UC 4 .SH NAME dd \- convert and copy a file --- 4,10 ---- .\" .\" @(#)dd.1 6.1 (Berkeley) 4/29/85 .\" ! .TH DD 1 "August 10, 2025" .UC 4 .SH NAME dd \- convert and copy a file *************** *** 12,18 **** .B dd [option=value] ... .SH DESCRIPTION ! .I Dd copies the specified input file to the specified output with possible conversions. --- 12,18 ---- .B dd [option=value] ... .SH DESCRIPTION ! .I dd copies the specified input file to the specified output with possible conversions. *************** *** 19,28 **** The standard input and output are used by default. The input and output block size may be specified to take advantage of raw physical I/O. .PP .br .ns ! .TP 15 .I option .I values .br --- 19,29 ---- The standard input and output are used by default. The input and output block size may be specified to take advantage of raw physical I/O. + By default, the output is truncated before copying. .PP .br .ns ! .TP 17 .I option .I values .br *************** *** 137,144 **** --- 138,175 ---- .I ibs .br .ns + .IP \*hnotrunc + do not truncate the output file + .br + .ns .IP "\*h... , ..." several comma-separated conversions + .br + .ns + .TP + status=noxfer + .ds h \h'\w'status='u' + do not print the transfer statistics as the last line of + status output + .br + .ns + .IP \*hnone + do not print the status output; error messages are shown but + informational messages are not + .br + .ns + .IP \*hprogress + print transfer statistics once per second + .br + .ns + .TP + .RI progress= n + switch on display of progress if + .I n + is set to any non-zero value; this will cause a "." to be printed + (to the standard error output) for every + .I n + full or partial blocks written to the output file .PP .fi Where sizes are specified, *** ./usr/src/man/man1/as.1.old Sat Mar 12 00:47:27 1994 --- ./usr/src/man/man1/as.1 Tue Aug 12 07:10:39 2025 *************** *** 5,10 **** --- 5,12 ---- .SH SYNOPSIS .B as [ + .B \- + ] [ .B \-u ] [ .B \-V *************** *** 16,21 **** --- 18,28 ---- .I As assembles the concatenation of the named files. The options are: + .TP + .B \- + Synonym with + .B \-u + for backwards compatibility earlier versions of the Unix PDP-11 assembler. .TP .B \-u Treat all undefined symbols in the assembly as external globals. *** ./usr/src/man/man3/printf.3.old Thu Apr 24 19:04:40 2025 --- ./usr/src/man/man3/printf.3 Mon Aug 11 10:41:07 2025 *************** *** 1,6 **** .\" @(#)printf.3s 6.5.2 (2.11BSD) 2025/04/24 .\" ! .TH PRINTF 3S "April 24, 2025" .AT 3 .SH NAME printf, fprintf, sprintf, vfprintf, vsprintf \- formatted output conversion --- 1,6 ---- .\" @(#)printf.3s 6.5.2 (2.11BSD) 2025/04/24 .\" ! .TH PRINTF 3S "August 11, 2025" .AT 3 .SH NAME printf, fprintf, sprintf, vfprintf, vsprintf \- formatted output conversion *************** *** 7,13 **** .SH SYNOPSIS .B #include .PP ! .B char *printf(format .RB [ , arg ] ... .B ) --- 7,13 ---- .SH SYNOPSIS .B #include .PP ! .B int printf(format .RB [ , arg ] ... .B ) *** ./VERSION.old Sun Aug 10 13:09:08 2025 --- ./VERSION Mon Aug 11 10:44:08 2025 *************** *** 1,5 **** ! Current Patch Level: 491 ! Date: August 10, 2025 2.11 BSD ============ --- 1,5 ---- ! Current Patch Level: 492 ! Date: August 12, 2025 2.11 BSD ============