„Czy kiedykolwiek zmieniłeś wartość 4?” - jak to się stało do quizu Hayes-Thomas?

24

W 1989 r. Felix Lee, John Hayes i Angela Thomas napisali test hakera w formie quizu z wieloma dowcipami poufnymi: „ Czy jesz śluzowce?

Rozważam następującą serię:

0015 Ever change the value of 4?
0016 ... Unintentionally?
0017 ... In a language other than Fortran?

Czy jest jakaś anegdota, która sprawia, że ​​liczba „4” jest szczególna w serii?

Czy niektóre implementacje Fortrana pozwoliły modyfikować wartość stałych? Czy było to możliwe w innych powszechnie używanych wówczas językach?

Michael Le Barbier Grünewald
źródło
2
@Ordous Nie mam nic przeciwko temu, aby zachować drugie pytanie tutaj, szczególnie jeśli odpowiadający starają się wyjaśnić, dlaczego takie zachowanie istnieje w nowoczesnych językach (tj. Czy są do tego praktyczne zastosowania?). To powiedziawszy, byłoby również doskonałym pytaniem Code Golf .
yannis
8
Powiązane: Napisz program, który sprawia, że ​​2 + 2 = 5 . Odpowiedzi Java i Python zastępują je 4na 5wewnętrznych listach liczb całkowitych.
Martijn Pieters
5
A komentarz na tej stronie stwierdza, że ​​można zmienić definicję literałów w FORTRAN IV; 4 = 5było możliwe.
Martijn Pieters
7
I dzięki za link testowy tego hakera. Sprawiłeś, że poczułam się stara, a także przerażona, jak często mogłam odpowiedzieć „tak” na pytania.
Martijn Pieters
5
Raz zmieniłem wartość stałego zera w programie fortran. To był bardzo trudny błąd do wyśledzenia.
Bryan Oakley

Odpowiedzi:

32

W dawnych czasach (lata siedemdziesiąte i wcześniej) niektóre komputery nie miały MMU (i tak jest dzisiaj w przypadku bardzo tanich mikrokontrolerów).

W takich systemach nie ma ochrony pamięci, więc nie ma segmentu tylko do odczytu w przestrzeni adresowej , a błędny program mógłby nadpisać stałą (albo w pamięci danych, albo nawet w kodzie maszynowym).

Kompilatory Fortran w tym czasie przekazały formalne argumenty przez odwołanie . Więc jeśli to zrobiłeś, CALL FUN(4)a SUBROUTINE FUN(I)jego ciało zmieniło się I- np. Z oświadczeniem I = I + 1w ciele, możesz mieć katastrofę, zmieniając 4 na 5 u dzwoniącego (lub gorzej).

Dotyczyło to również pierwszych mikrokomputerów, takich jak oryginalny IBM PC AT z 1984 r., Z MS-DOS

FWIW, jestem na tyle dorosły, że jako nastolatka użyłem na początku lat 70. takich komputerów: IBM1620 i CAB500 (w muzeum: są to komputery z lat 60.!). IBM1620 był całkiem zabawny: był używany w tabelach pamięci do dodawania i mnożenia (a jeśli nadpisałeś te tabele, nastąpił chaos). Więc nie tylko możesz zastąpić 4, ale możesz nawet zastąpić każde przyszłe dodanie 2 + 2 lub 7 * 8 mnożenia (ale tak naprawdę zapomniałem tych brudnych szczegółów, więc może się mylić).

Dzisiaj możesz nadpisać kod BIOS w pamięci flash, jeśli będziesz wystarczająco wytrwały. Niestety nie czuję już takiej zabawy, więc nigdy nie próbowałem. (Boję się nawet instalować trochę LinuxBios na mojej płycie głównej).

Na obecnych komputerach i systemach operacyjnych przekazywanie stałej przez odniesienie i zmienianie jej wewnątrz odbiorcy spowoduje po prostu naruszenie segmentacji , co brzmi znajomo dla wielu programistów C lub C ++.

BTW: być głupim: nadpisywanie 4 nie jest kwestią języka, ale implementacji.

Basile Starynkevitch
źródło
14
Model 1620 został nazwany CADET: nie można dodawać, nawet nie próbuje.
Pete Becker
Sztuczka może być prawie powtórzona nawet teraz gfortran. Stałe są umieszczane w swoim segmencie i przekazywane przez odniesienie do podprogramu. Domyślnie sekcja stała jest tylko do odczytu, więc błąd ochrony pamięci zabija program.
Netch
7

Był to niezamierzony efekt uboczny strategii oceny wywołań funkcji FORTRAN w połączeniu z błędną optymalizacją kompilatora.

FORTRAN II wprowadził zdefiniowane przez użytkownika funkcje i podprogramy, których argumenty przekazywane są przez odwołanie . (No nie wiem. Prawdopodobnie było to wtedy bardziej wydajne niż przekazywanie wartości na sprzęcie IBM.)

Zwykle przekazywanie przez odniesienie oznacza, że ​​musisz przekazać wartość l (jak zmienna) zamiast wartości r. Ale projektanci FORTRAN postanowili być pomocni i pozwolić ci przekazać wartości r jako argumenty. Kompilator automatycznie wygeneruje dla ciebie zmienną. Więc jeśli napisałeś:

CALL SUBFOO(X + Y, 4)

kompilator przekształciłby to za kulisami w coś podobnego

TEMP1 = X + Y
TEMP2 = 4
CALL SUBFOO(TEMP1, TEMP2)

Istniała również wspólna optymalizacja kompilatora zwana „pulą literałów”, która konsolidowałaby wiele wystąpień tej samej stałej liczbowej w tej samej automatycznie generowanej zmiennej. (Kilka języków w rodzinie C wymaga tego do literałów łańcuchowych.) Więc jeśli napisałeś

CALL SUBBAR(4)
CALL SUBBAZ(4)

byłoby to traktowane tak, jakby tak było

FOUR = 4
CALL SUBBAR(FOUR)
CALL SUBBAZ(FOUR)

co wydaje się całkowicie rozsądną rzeczą, dopóki nie pojawi się podprogram, który zmienia wartość jego parametrów.

SUBROUTINE SUBBAR(X)
    !...lots of code...
    X = 5
    !...lots of code...
END SUBROUTINE SUBBAR

Bum! CALL SUBBAR(4)zmieniłem wartość 4 w literalnej puli na 5. A potem zastanawiasz się, dlaczego SUBBAZzakładasz, że przekazałeś jej 5 zamiast 4faktycznie napisanej w kodzie.

Nowsze wersje Fortran łagodzą ten problem, pozwalając zadeklarować INTENTzmienną jako INlub OUTi dając ci błąd (lub przynajmniej ostrzeżenie), jeśli przekażesz stałą jako OUTparametr.

dan04
źródło
5

W FORTRAN, gdy stała jest przekazywana do innej procedury, nie jest już chroniona. Do tego się odnoszą. Inne popularne języki programowania w tym samym czasie to C i Pascal, które nie miały (i nadal nie mają) tego problemu. Być może istnieją starsze języki programowania, o których nie wiem, że mają ten sam problem.

dj bazzie wazzie
źródło
Odnosi się także do faktu, że stała pula nie była w segmencie tylko do odczytu. Gdyby tak było, a 4 zostanie przekazane przez odniesienie i zmienione przez odbiorcę, SEGV nastąpiłoby bez pomyślnej zmiany 4.
Basile Starynkevitch
Jest tak, ponieważ nie każdy system operacyjny miał segment tylko do odczytu. Pułapkę można wykorzystać na przykład w systemie DOS, systemy operacyjne z segmentami tylko do odczytu (wykorzystującymi pamięć wirtualną), takie jak UNIX, zwracają błąd błędu segmentacji w czasie wykonywania. W każdym razie kompilator nie powinien na to pozwolić.
dj bazzie wazzie
4
Tęsknię za Pascalem :(
Gareth
1
Mówiąc ściślej, FORTRAN przechodzi przez odniesienie. Jeśli więc przekażesz stałą jako parametr funkcji, możesz zmienić tę wartość przy każdym użyciu tej liczby.
Gabe
1
Tylko jeśli ta stała (przekazana przez referencję) pozostaje w segmencie odczytu i zapisu. Jeśli jest w .rodatasegmencie tylko do odczytu (jak robią to obecne kompilatory), zmiana nie zmieni stałej, ale spowoduje SEGV.
Basile Starynkevitch