An initial analysis of the discovered Unix V4 tape
https://news.ycombinator.com/item?id=46367744
Unix v4 (1973) – Live Terminal
+ register int i;
q = password;
- while((*q = getchar()) != '\n')
+ i = 0;
+ while((*q = getchar()) != '\n') {
+ if (++i >= sizeof(password))
+ goto error;
You don't actually need i here. i is the same as (q - password). It would be idiomatic C to simply rewrite the loop condition as: while (q < password+sizeof(password) && (*q = getchar()) != '\n'). To preserve your "goto error;" part, maybe you could do the overflow check when null terminating outside the loop.The author did try pointer arithmetic:
> I initially attempted a fix using pointer arithmetic, but the 1973 C compiler didn’t like it, while it didn’t refuse the syntax, the code had no effect.
I missed the blurb about pointer arithmetic. Would be interesting to go into detail about what "had no effect" means.
> We will edit su.c to prevent the overflow by maintaining a counter, i, and verifying it against the buffer size during the read loop. I initially attempted a fix using pointer arithmetic, but the 1973 C compiler didn’t like it, while it didn’t refuse the syntax, the code had no effect. I settled on a simpler index-based check instead.
There's no details.. why didn't it work? Maybe the author didn't write it correctly? No word on what the assembly output for it was...
Not the million line codebases we have today. 50-100 lines was the usual program or script.
So likely they would work on the printout:
1,$n
And then input the corrections into ed(1).It's really not bad, and you can use it for scripting like sed, but it's clunkier.
extern fin;
if(getpw(0, pwbuf))
goto badpw;
(&fin)[1] = 0; extern FILE *stdin;There may have been a early C without structs (B had none,) but according to Ken Thompson, the addition of structs to C was an important change, and a reason why his third attempt rewrite UNIX from assembly to a portable language finally succeeded. Certainly by the time the recently recovered v4 tape was made, C had structs:
~/unix_v4$ cat usr/sys/proc.h
struct proc {
char p_stat;
char p_flag;
char p_pri;
char p_sig;
char p_null;
char p_time;
int p_ttyp;
int p_pid;
int p_ppid;
int p_addr;
int p_size;
int p_wchan;
int *p_textp;
} proc[NPROC];
/* stat codes */
#define SSLEEP 1
#define SWAIT 2
#define SRUN 3
#define SIDL 4
#define SZOMB 5
/* flag codes */
#define SLOAD 01
#define SSYS 02
#define SLOCK 04
#define SSWAP 010I took a different tack. The buffer was allocated with malloc. When a string was larger, it was realloced to a larger size. This worked until memory was exhausted, and then the program quit.
It was actually less code to implement than having a fixed size buffer.
Ditto for the other compilation limits, such as length of a line. The only limit was running out of memory.
If so, could you type the same password that’s exactly 100 bytes twice and then hit enter to gain root? With only clobbering one additional byte, of ttybuf?
Edit: no, silly, password is overwritten with its hash before the comparison.
Right.
> If so, could you type the same password that’s exactly 100 bytes twice and then hit enter to gain root? With only clobbering one additional byte, of ttybuf?
Almost. You need to type crypt(password) in the part that overflows to pwbuf.
What compiler are you using?
ttybuf[2] =& ~010;
Which is another bug.
# ... sound of crickets ...
Wanna see me do it again?