10
\$\begingroup\$

Background

While a program is running on a computer, the computer needs to keep track of that program somehow, typically by either copying or mapping the compiled form of the program into memory. (Even interpreted languages do this; a few primitive languages work with source code directly, but in most interpreted languages, the a running program in memory is stored in some sort of bytecode.) We'll call this the program's "image in memory", or just "image" for short. The important property that defines the image as a memory image is that if we modify it while the program is running, then it's the newly modified image that's running (because it's the image that's used to remember what it is that's running).

Now, the fact that this image is in memory means that it can be modified. (Unless the interpreter/runtime is rereading the program from disk as it executes every command, which would be a very unusual design, changing the file on disk won't change the image; likewise, this means that we're typically going to be able to change the image without affecting the disk file.) Most operating systems nowadays have a security feature (known as "W^X") that prevents altering a memory image of a currently running program, but they also normally have a way to turn it off (and it only applies to programs being executed directly by the OS, not programs whose image is being run by an interpreter).

The task

Write a program that modifies the portion of memory storing its own image so that it becomes all-bits-zero. (At this point, the interpreter is fairly likely to crash or enter an infinite loop, because an all-bits-zero program isn't very meaningful.) The difficulty in this task is mostly that as you write more and more zeroes to memory, less and less of your program will still be intact and runnable. The very last command of the original program that runs must, of course, overwrite itself.

To prevent degenerate solutions (such as the null program) being valid, your program's memory image must contain at least 31 contiguous bytes which will have no effect on the execution of the program, regardless of their values (i.e. they're overwritten with zeroes before the first time they execute). (Programming Puzzles & Code Golf will fit neatly into the gap, although you might want to use some other padding to save bytes.) This means that unless someone manages to find a pre-existing programming language in which all 31-byte programs self-annihilate, the best possible score on this challenge for a memory image submission will be 32 bytes. (A source code submission could be shorter, if it compiles to an image at least 32 bytes long.)

Clarifications and additional restrictions

  • If W^X or a similar security feature would prevents writes to the portion of memory you would need to write to, include instructions on how to turn it off. You're allowed to turn it off via any of building the program in an unusual way, running the program in an unusual way, or adding code to the program to request turning off W^X. In the last case, the added code does not count towards the length of the program, so long as the program would run without it on a hypothetical version of your operating system which had no restrictions on writing to memory.
  • It doesn't matter what happens after the program's image in memory is zeroed; you don't have control over that anyway.
  • In cases where it matters (e.g. C programs loaded from disk lazily by the MMU), we care about the logical addresses which hold the program's image, not the physical addresses. In particular, you're allowed to unmap part of the program and remap zeroes over it, so long as (obviously) that part of the program isn't running at the time. However, to avoid degenerate solutions, you are not allowed to atomically remap a section of memory; when remapping memory, it must spend some nonzero period of time unmapped in between.
  • You only need to zero the executable portions of your program's image (plus the 31 arbitrary bytes that prevent degenerate solutions). Note that this is based on what actually gets executed, not based on what the program claims will get executed, i.e. if you send the instruction pointer to an unusual place like the data segment or the stack, you'll have to zero that place too.
  • The previous restriction also applies to library functions, and the like; if you execute those, and doing so causes the library function to be interpreted (by an interpreter or by the operating system) in the same way as your program itself, you're going to have to zero its memory image too. It's not interesting or fun to just call a memset equivalent to set your program's entire memory image to zero, and have it work because the instruction pointer is inside memset at the time rather than inside the original program.
  • The 31 arbitrary bytes only need to be unread while the program's actually running. If your language has some sort of bytecode verification step (which would reject arbitrary bytes in the memory image), feel free to choose the values of the bytes as something that would pass verification, so long as the values could subsequently be changed without affecting the operation of the program in any way. (You also don't need to write the program's source code in such a way that it'd allow arbitrary values for the bytes; you can assume someone edits them on disk or in memory after any compilation, parsing, etc. stages have already happened.)
  • A few interpreters (mostly for concatenative languages, because this clearly doesn't work in languages that have loop constructs) naturally delete the program's image as it runs. Those aren't eligible for this challenge.

Victory condition

This is at least partly a challenge to see how many languages something like this is possible in; feel free to try to solve it using a language that hasn't been used yet, or to find a new way to accomplish this task. However, to promote competition and give some objective way to compare similar solutions, victory for this challenge is based on the common "shorter (in bytes) is better" rule, making this . You can measure the byte count of either your program's memory image, or your program's source code, although make sure you correctly label what's being counted in the post title (e.g. a source code submission would look like "C" or "Perl", whereas an image submission would look more like "x86_64 machine code" or "Perl bytecode").

\$\endgroup\$
10
  • \$\begingroup\$ If you're looking for a challenge about deleting the executable from disk (rather than the image of the executable from memory), that's this one. If you're looking for the Sandbox post (and can see deleted posts), that's here. \$\endgroup\$
    – user62131
    May 7 '17 at 2:20
  • \$\begingroup\$ Are you sure this is so difficult? Typical modern processors have an instruction cache, so the new overwritten version of the memory need not be used by the processor. \$\endgroup\$
    – feersum
    May 7 '17 at 2:32
  • \$\begingroup\$ @feersum: Unfortunately there's not much I can do to shut down that sort of thing. However, the modern processors I've worked with also flush the instruction cache if the memory image they're executing from gets modified (so although there's a cache, it's not usable for this sort of challenge); it wouldn't surprise me if there were some that don't, but identifying them should be at least a little nontrivial. The challenge would still remain open and competitive for interpreted languages, which rarely do that sort of caching. \$\endgroup\$
    – user62131
    May 7 '17 at 2:38
  • \$\begingroup\$ So.. Delete itself from the RAM? \$\endgroup\$ May 7 '17 at 3:45
  • 2
    \$\begingroup\$ "Unless the interpreter/runtime is rereading the program from disk as it executes every command, which would be a very unusual design" -- Windows Batch does this and "unusual" is the least profanity-ridden way in which this feature has been described. \$\endgroup\$ May 7 '17 at 6:49