1
0
mirror of https://github.com/wfjm/w11.git synced 2026-01-21 10:42:10 +00:00

stktst: minor updates, add README and data [skip ci]

- stktst.c: add (s,c,o) breakdown of sp address
- dotst.s: skip -c and -s for negative counts
This commit is contained in:
wfjm 2022-08-04 09:11:33 +02:00
parent 6d98a17e86
commit 1be7bb0376
5 changed files with 219 additions and 11 deletions

View File

@ -1,4 +1,4 @@
# $Id: $
# $Id: Makefile 1267 2022-08-02 06:27:29Z mueller $
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright 2022- by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
#

View File

@ -0,0 +1,83 @@
## stktst: a program testing 2.11BSD stack extension logic
The `stktst` program exercises the 2.11BSD stack extension logic.
In a first step, the `sp` can be aligned to a click (64 byte) or a
segment (8129 byte) boundary.
An offset can also be applied after this alignment.
In a second step, a sequence of integer and floating point instructions with
a `-(sp)` destination is executed.
This allows to set up almost every possible stack extension situation.
Motivation for `stktst` were differences in the `MMR1` register implementation
in different PDP-11 CPUs, and differences in the modeling of `MMR1` in
different PDP-11 simulators.
That combined with the 2.11BSD stack extension handling prior to #473 can lead
to unexpected "Memory fault" aborts in 2.11BSD.
The results are collected in the [data](data) folder.
`stktst` has an assembler core [dotst.s](dotst.s) which is called from a
C main program [stktst.c](stktst.c). It is called as
```
./stktst <cmd> <count> [-s ns] [-c nc] [-o no]
```
The options control the initial stack alignment:
- **`-s ns`**: aligns to 8192 byte segment boundaries. ns=1 to the next one,
nc=2 to the second next one, etc.
Obviously, `ns` should be smaller than 8.
The option is ignored if ns<=0.
- **`-c nc`**: aligns to 64 byte click boundaries. nc=1 to the next one,
nc=2 to the second next one, etc.
The first alignment step will not change the stack if it was alreay on a
click bounday, it will therefore add 0 to 64 bytes to the stack.
The option is ignored if nc<=0.
Click alignment is done after segment alignment.
- **`-o no`**: adds `no` to `sp`. `no` must be even and can be positive or
negative.
**Notes**:
- no range checks is done for `no`.
After a **-s** is is save to use small positive `no` values to position `sp`
a bit before a segment boundary.
After a **-c** is is better to use negative `no` values to position `sp`
before the next click boundary.
- the stack allocated below the argument and environment values.
The initial `sp` value will therefore decrease when the number of characters
in the argument list increases because the stack base moves down.
In some cases it is therefore prudent to specify the numbers as quoted
strings with some leading blanks, like
```
./stktst d ' 3' -c ' 2' -o ' 4'
```
That allows to change the counts without changing the length of the
argument list.
The `cmd` argument selects the instruction that does the stack push and
`count` determines how often it is executed.
The available modes for `cmd` are
- **`I`**: use `clr -(sp)` --> integer word push
- **`i`**: use `movfi -(sp)` after `seti` --> word push from FPP
- **`l`**: use `movfi -(sp)` after `setl` --> double word push from FPP
- **`f`**: use `movf -(sp)` after `setf` --> double word push from FPP
- **`d`**: use `movf -(sp)` after `setd` --> quad word push from FPP
For debug purposes three additional `cmd` modes are available:
- **`r`**: uses `count` as an address and reads
- **`w`**: uses `count` as an address, reads and re-writes
- **`h`**: runs a `halt`
`stktst` prints the `sp` after alignment and after the stack pushes like
```
stktst-I: before sp 177304 (0, 4,60); 177200 (0, 5,64);
stktst-I: after sp 177304 (0, 4,60); 177200 (0, 5,64); 167200 (0, 69,64);
```
and gives the `sp` value
- after `dotst.s` is called
- after alignments and offsets were applied
- after stack pushes were executed
and prints it in octal and broken down in segment, click and byte offset.
Because the stack is a downward growing segment, all offsets measure the
distance to the top of memory and increase when the `sp` decreases.
When a stack extension fails, the program will print the first line and abort.

View File

@ -0,0 +1,109 @@
## 2022-08-03: tests with SimH V3.11-0 and 2.11BSD 447 (plus 453 patch)
### Background
The `MMR1` response after an MMU abort in an FPP instruction depends on the CPU.
In an 11/70, the registers reflect the state at abort and `MMR1` shows the
change. In a J11, the registers are unchanged, and `MMR1` shows zero.
SimH V3.11-0 used the FPP MMU abort handling for _all_ CPU models.
So even when an 11/70 is modeled, the behavior is like a J11.
The 2.11BSD stack extension logic checks whether the `sp` is below the
current stack allocation, and only in that case, the stack is extended.
On a J11-based system that fails, that's why 2.11BSD up to #473 has a
workaround and shifts the `sp` down by 4 _if and only if_ a J11 was
probed at boot time. The pertinent code in `/usr/src/sys/pdp/trap.c` is
```
osp = sp;
if (kdj11)
osp -= 4;
if (backup(u.u_ar0) == 0)
if (!(u.u_sigstk.ss_flags & SA_ONSTACK) && grow((u_int)osp))
goto out;
i = SIGSEGV;
```
That leads to two vulnerabilities:
- a `double` push of 8 bytes might fail on J11 systems, real or simulated,
- in a SimH 11/70, which probes as an 11/70 but behaves like a J11, any
push from an FPP instruction might fail.
The first is a 2.11BSD issue, the second is a SimH issue.
The tests were run under `tcsh`, it gives "Segmentation fault" in case of
a problem. Under `sh` one gets "Memory fault".
### SimH in 11/94 mode
SimH pdp11 started with
```
set cpu 11/94
```
and 2.11BSD starts with
```
94Boot from xp(0,0,0) at 0176700
```
Extending the stack with `float` pushes is no problem until the stack segment
has grown to 020000 and memory is really exhausted:
```
./stktst f ' 1024'
# stktst-I: before sp 177334 (0, 4,36); 177334 (0, 4,36);
# stktst-I: after sp 177334 (0, 4,36); 177334 (0, 4,36); 167334 (0, 68,36);
./stktst f '14263'
# stktst-I: before sp 177334 (0, 4,36); 177334 (0, 4,36);
# stktst-I: after sp 177334 (0, 4,36); 177334 (0, 4,36); 020000 (6,127,64);
./stktst f '14264'
# stktst-I: before sp 177334 (0, 4,36); 177334 (0, 4,36);
# Segmentation fault (core dumped)
```
Extending with `double` pushes works when the `double` is aligned on an
8 byte border. In this case the 4 byte correction done in 2.11BSD #473
ensures that the `sp` is interpreted as below current allocation.
The alignment is set up with `-c 2 -o 0`:
```
./stktst d ' 512' -c ' 2' -o ' 0'
# stktst-I: before sp 177304 (0, 4,60); 177200 (0, 5,64);
# stktst-I: after sp 177304 (0, 4,60); 177200 (0, 5,64); 167200 (0, 69,64);
./stktst d '7120' -c ' 2' -o ' 0'
# stktst-I: before sp 177304 (0, 4,60); 177200 (0, 5,64);
# stktst-I: after sp 177304 (0, 4,60); 177200 (0, 5,64); 020000 (6,127,64);
./stktst d '7121' -c ' 2' -o ' 0'
# stktst-I: before sp 177304 (0, 4,60); 177200 (0, 5,64);
# Segmentation fault (core dumped)
```
Misaligned `double` pushes fail as expected.
A 4 byte misalignment is set up with `-c 2 -o -4`.
Even with the 4 byte correction the `sp` appears to be in the allocated area,
and a `SIGSEGV` is taken:
```
./stktst d ' 1' -c ' 2' -o ' -4'
# stktst-I: before sp 177304 (0, 4,60); 177174 (0, 6, 4);
# stktst-I: after sp 177304 (0, 4,60); 177174 (0, 6, 4); 177164 (0, 6,12);
./stktst d ' 319' -c ' 2' -o ' -4'
# stktst-I: before sp 177304 (0, 4,60); 177174 (0, 6, 4);
# stktst-I: after sp 177304 (0, 4,60); 177174 (0, 6, 4); 172204 (0, 45,60);
./stktst d ' 320' -c ' 2' -o ' -4'
# stktst-I: before sp 177304 (0, 4,60); 177174 (0, 6, 4);
# Segmentation fault (core dumped)
```
The initial stack size is 20 clicks
(see [SSIZE](https://www.retro11.de/ouxr/211bsd/usr/src/sys/pdp/machparam.h.html#m:SSIZE)),
the stack increment is also 20 clicks
(see [SINCR](https://www.retro11.de/ouxr/211bsd/usr/src/sys/pdp/machparam.h.html#m:SINCR)).
The initial stack segment size is a bit larger due to argument and environment
allocations.
In the tests it was 24 clicks as tests with the **r** command show:
```
./stktst r 0175004
# OK
./stktst r 0175000
# OK
./stktst r 0174776
# Segmentation fault (core dumped)
```
It is therefore a bit surprising that the failure happens at the border
expected for the second stack extend and not around 0175000.

View File

@ -1,4 +1,4 @@
/ $Id: $
/ $Id: dotst.s 1268 2022-08-04 07:03:08Z mueller $
/ SPDX-License-Identifier: GPL-3.0-or-later
/ Copyright 2022- by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
/
@ -17,7 +17,7 @@
/
/ Revision History:
/ Date Rev Version Comment
/ 2022-08-01 1267 1.0 Initial version
/ 2022-08-03 1268 1.0 Initial version
/
.globl _dotst
@ -82,7 +82,7 @@ testx:
mov sp,r4
/ handle -s
tst 6(r2) / idat[3] -s count > 0 ?
beq optc
ble optc
bic $017777,r4 / sp &= 017777
mov 6(r2),r1
dec r1 / idat[3]-1
@ -90,7 +90,7 @@ testx:
sub r1,r4 / sp -= 8192 * (idat[3]-1)
/ handle -c
optc: tst 4(r2) / idat[2] -c count > 0 ?
beq opto
ble opto
bic $000077,r4 / sp &= 077
mov 4(r2),r1
dec r1 / idat[2]-1

View File

@ -1,10 +1,10 @@
/* $Id: $ */
/* $Id: stktst.c 1268 2022-08-04 07:03:08Z mueller $ */
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright 2022- by Walter F.J. Mueller <W.F.J.Mueller@gsi.de> */
/* Revision History: */
/* Date Rev Version Comment */
/* 2022-08-01 1267 1.0 Initial version */
/* 2022-08-03 1268 1.0 Initial version */
#include <stdlib.h>
#include <stdio.h>
@ -37,6 +37,15 @@ int doconv(arg)
return ires;
}
print_stack(val)
unsigned int val;
{
unsigned int ns = 7-(val>>13);
unsigned int nc = 127-((val&017777)>>6);
unsigned int no = 64-(val&077);
fprintf(stdout," %06o (%1d,%3d,%2d);", val, ns, nc, no);
}
int main(argc, argv)
int argc;
@ -47,7 +56,7 @@ int main(argc, argv)
int rcnt = 0;
int cnt = 0;
int idat[5];
int odat[3];
unsigned int odat[3];
int optrwh = 0;
int dotst();
@ -105,12 +114,19 @@ int main(argc, argv)
/* call test: 1st round */
dotst(idat, odat);
if (optrwh) exit(0);
fprintf(stdout, "stktst-I: before sp %06o %06o\n", odat[0], odat[1]);
printf("stktst-I: before sp");
print_stack(odat[0]);
print_stack(odat[1]);
printf("\n");
/* call test: 2nd round */
idat[1] = rcnt;
dotst(idat, odat);
fprintf(stdout, "stktst-I: after sp %06o %06o %6o\n",
odat[0], odat[1], odat[2]);
printf("stktst-I: after sp");
print_stack(odat[0]);
print_stack(odat[1]);
print_stack(odat[2]);
printf("\n");
exit(0);
}