# [Assembly (MIPS, SPIM)], interpreter bug, 15 bytes

    main:sb $0 f
    f:

[Try it online!][TIO-la6xvmyb]

Here's a cute one I found by accident.


This attempts to store a byte to the address at label `f`. This is in the `.text` section.


SPIM is pretty clever in that writing out of bounds of a section will just grow the section. However, writing to `.text` is tricky.

SPIM stores instructions in a struct for easier parsing, instead of just storing data directly. 

`CPU/inst.h:57`:
```lang-cpp
/* Representation of an instruction. Store the instruction fields in an
   overlapping manner similar to the real encoding (but not identical, to
   speed decoding in C code, as opposed to hardware).. */

typedef struct inst_s
{
  short opcode;

  union
    {
      // Snip
    } r_t;

  int32 encoding;
  imm_expr *expr;
  char *source_line;
} instruction;
```

Therefore, to store to `.text`, SPIM will give it a special treatment in `bad_mem_write()`.

`CPU/mem.cpp:395`:
```lang-cpp
void
set_mem_byte(mem_addr addr, reg_word value)
{
  data_modified = true;
  // .data
  if ((addr >= DATA_BOT) && (addr < data_top))
    data_seg_b [addr - DATA_BOT] = (BYTE_TYPE) value;
  // .stack
  else if ((addr >= stack_bot) && (addr < STACK_TOP))
    stack_seg_b [addr - stack_bot] = (BYTE_TYPE) value;
  // .data
  else if ((addr >= K_DATA_BOT) && (addr < k_data_top))
    k_data_seg_b [addr - K_DATA_BOT] = (BYTE_TYPE) value;
  // .text or section out of bounds
  else
    bad_mem_write (addr, value, 0); // <--
}
```

In `bad_mem_write()`, SPIM will to splice and recompile the instruction.
`CPU/mem.cpp:506`:
```lang-cpp
static void
bad_mem_write (mem_addr addr, mem_word value, int mask)
{
  mem_word tmp;

  if ((addr & mask) != 0)
    /* Unaligned address fault */
    RAISE_EXCEPTION (ExcCode_AdES, CP0_BadVAddr = addr)
    else if (addr >= TEXT_BOT && addr < text_top)
  {
    // For halfword and byte writes, attempt to overwrite part of the instruction.
    switch (mask)
    {
    case 0x0:
      tmp = ENCODING (text_seg [(addr - TEXT_BOT) >> 2]);

#ifdef SPIM_BIGENDIAN
      tmp = ((tmp & ~(0xff << (8 * (3 - (addr & 0x3)))))
               | (value & 0xff) << (8 * (3 - (addr & 0x3))));
#else
      tmp = ((tmp & ~(0xff << (8 * (addr & 0x3))))
               | (value & 0xff) << (8 * (addr & 0x3)));
#endif
      break;
      // ...
    }

    // Free instruction if it isn't NULL
    if (text_seg [(addr - TEXT_BOT) >> 2] != NULL)
    {
      free_inst (text_seg[(addr - TEXT_BOT) >> 2]);
    }
    // create a new instruction with the encoding
    text_seg [(addr - TEXT_BOT) >> 2] = inst_decode (tmp);
```

But wait a second....

SPIM checks for NULL...
`CPU/mem.cpp:549`:
```lang-c
if (text_seg [(addr - TEXT_BOT) >> 2] != NULL)
```
...after it already dereferenced it!

`CPU/mem.cpp:519` (after macro expansion)
```lang-cpp
tmp = text_seg [(addr - TEXT_BOT) >> 2]->encoding;
```
And what would cause this instruction to be NULL? If it is not an instruction.
And what is at the label `f`? Nothing, so SPIM dereferences a NULL pointer.


[Assembly (MIPS, SPIM)]: https://github.com/TryItOnline/spim
[TIO-la6xvmyb]: https://tio.run/##Ky7IzP3/PzcxM8@qOElBxUAhjSvN6v9/AA "Assembly (MIPS, SPIM) – Try It Online"