25

In 1989 Felix Lee, John Hayes and Angela Thomas wrote a Hacker's test taking the form of a quiz with many insider jokes, as “Do you eat slime-molds?

I am considering the following series:

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

Is there a particular anecdote making the number “4” particular in the series?

Did some Fortran implementation allow to modify the value of constants? Was this possible in other languages in common use at that time?

8
  • 2
    @Ordous I don't mind if we keep the second question here, especially if answerers take care to explain why such a behaviour exists in modern languages (i.e. are there any practical uses for it?). That said, it would also make for an excellent Code Golf question.
    – yannis
    Aug 29, 2014 at 10:50
  • 8
    Related: Write a program that makes 2 + 2 = 5. A Java and Python answers there replace 4 for 5 in the interned integers lists. Aug 29, 2014 at 10:57
  • 5
    And a comment on that page states that you could redefine literals in FORTRAN IV; 4 = 5 was possible. Aug 29, 2014 at 11:04
  • 7
    And thanks for that Hacker's test link. You now made me feel old, as well as horrified on how often I could answer 'yes' to the questions. Aug 29, 2014 at 11:14
  • 5
    I changed the value of the constant zero in a fortran program once. That was a very difficult bug to track down. Aug 29, 2014 at 18:35

3 Answers 3

34

In the old days (1970s and before) some computers did not have any MMU (and this is true today for very cheap microcontrollers).

On such systems, there is no memory protection so no read-only segment in the address space, and a buggy program could overwrite a constant (either in data memory, or even inside the machine code).

The Fortran compilers at that time passed formal arguments by reference. So if you did CALL FUN(4) and the SUBROUTINE FUN(I) has its body changing I - e.g. with an statement I = I + 1 in its body, you could have a disaster, changing 4 into 5 in the caller (or worse).

This was also true on the first microcomputers like the original IBM PC AT from 1984, with MS-DOS

FWIW, I'm old enough to have used, as a teen ager in early 1970s, such computers: IBM1620 and CAB500 (in a museum: these are 1960s era computers!). The IBM1620 was quite fun: it used in memory tables for additions and multiplications (and if you overwrote these tables, chaos ensued). So not only you could overwrite a 4, but you could even overwrite every future 2+2 addition or 7*8 multiplications (but I really forgot these dirty details so could be wrong).

Today, you might overwrite the BIOS code in flash memory, if you are persevering enough. Sadly, I don't feel that fun any more, so I never tried. (I'm even afraid of installing some LinuxBios on my motherboard).

On current computers and operating systems passing a constant by reference and changing it inside the callee will just provoke a segmentation violation, which sounds familiar to many C or C++ developers.

BTW: to be nitpicking: overwriting 4 is not a matter of language, but of implementation.

2
  • 14
    The 1620 was nicknamed CADET: Can't Add, Doesn't Even Try. Aug 29, 2014 at 15:55
  • 1
    The trick can be almost repeated even now with gfortran. Constants are put into their segment and passed by reference to a subroutine. By default, the constant section is read-only, so memory protection error kills the program.
    – Netch
    Aug 30, 2014 at 6:33
12

It was an unintentional side effect of FORTRAN's function call evaluation strategy in combination with an erroneous compiler optimization.

FORTRAN II introduced user-defined functions and subroutines with their arguments passed by reference. (Why, I don't know. It was probably more efficient than pass-by-value on IBM hardware of the time.)

Normally, pass-by-reference means you have to have to pass an l-value (like a variable) instead of an r-value. But the designers of FORTRAN decided to be helpful and let you pass r-values as arguments anyway. The compiler would automatically generate a variable for you. So, if you wrote:

CALL SUBFOO(X + Y, 4)

the compiler would convert this behind the scenes to something like

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

There was also a common compiler optimization called a “literal pool”, that would consolidate multiple instances of the same numeric constant into the same auto-generated variable. (Several languages in the C family require this for string literals.) So, if you wrote

CALL SUBBAR(4)
CALL SUBBAZ(4)

this would be treated as if it were

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

which seems like a perfectly reasonable thing to do until you have a subprogram that changes the value of its parameters.

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

Boom! CALL SUBBAR(4) changed the value of the 4 in the literal pool to a 5. And then you're left wondering why SUBBAZ is assuming you passed it a 5 instead of the 4 you actually wrote in the code.

Newer versions of Fortran mitigate this problem by letting you declare the INTENT of a variable as IN or OUT, and giving you an error (or at least a warning) if you pass a constant as an OUT parameter.

5

In FORTRAN, when a constant is passed to another procedure, it is no longer protected. That is what they refer to. Other popular programming languages in that same time were C and Pascal which didn't (and still don't) have this issue. Maybe there are older programming languages which I'm not aware of that have the same issue.

5
  • Also, it refers to the fact that the constant pool was not in a read-only segment. If it did, and 4 is passed by reference, and changed by the callee, SEGV would happen without successfully changing 4. Aug 29, 2014 at 11:19
  • That is because not every OS had a read-only segment. The pitfall could be used on DOS for instance, operating systems with read-only segments (using virtual memory) like UNIX would return an segmentation fault error at run time. Anyway, the compiler should not allow it. Aug 29, 2014 at 11:30
  • 4
    I miss Pascal :(
    – Gareth
    Aug 29, 2014 at 14:23
  • 1
    To be more specific, FORTRAN passes by reference. So if you pass a constant as a function parameter, you can change that value for every use of that number.
    – Gabe
    Aug 29, 2014 at 15:17
  • 1
    Only if that constant (passed by reference) stays in a read-write segment. If it is in a .rodata read-only segment (as current compilers do) changing it won't alter the constant but would cause a SEGV. Aug 29, 2014 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.