Skip to content
This repository has been archived by the owner on Jan 24, 2022. It is now read-only.

Zero cost stack overflow protection, take 2 #43

Merged
merged 6 commits into from
Feb 17, 2018
Merged

Zero cost stack overflow protection, take 2 #43

merged 6 commits into from
Feb 17, 2018

Conversation

japaric
Copy link
Member

@japaric japaric commented Nov 9, 2017

This is one possible solution to the stack overflow problem described in #34. This approach uses a
linker wrapper, called swap-ld, to generate the desired memory layout. See #34 for a description
of the desired memory layout and #41 for a description of how swap-ld works.

The observable effects of this change in cortex-m programs are:

  • the _sbss symbol is now override-able.
  • there is now a .stack linker section that denotes the span of the call stack. .stack won't be
    loaded into the program; it just exists for informative purposes (swap-ld uses this
    information).

Given the following program:

fn main() {
    static mut X: u32 = 0;
    static mut Y: u32 = 1;

    loop {
        unsafe {
            ptr::write_volatile(&mut X, X + 1);
            ptr::write_volatile(&mut Y, Y + 1);
        }
    }
}

If you link this program using the arm-none-eabi-ld linker, which is the cortex-m-quickstart
default, you'll get the following memory layout:

$ console
section                                                                   size         addr
.vector_table                                                            0x130    0x8000000
.text                                                                     0x94    0x8000130
.rodata                                                                    0x0    0x80001c4
.stack                                                                  0x5000   0x20000000
.bss                                                                       0x4   0x20000000
.data                                                                      0x4   0x20000004

Note how the space reserved for the stack (depicted by the .stack linker section) overlaps with
the space where .bss and .data reside.

If you, instead, link this program using swap-ld you'll get the following memory layout:

$ arm-none-eabi-size -Ax app
section                                                                   size         addr
.vector_table                                                            0x130    0x8000000
.text                                                                     0x94    0x8000130
.rodata                                                                    0x0    0x80001c4
.stack                                                                   0x4ff8   0x20000000
.bss                                                                       0x4   0x20004ff8
.data                                                                      0x4   0x20004ffc

Note that no overlap exists in this case and that the call stack size has reduced to accommodate the
.bss and .data sections.

Unlike #41 the addresses of static variables is now correct:

$ arm-none-eabi-objdump -CD app
Disassembly of section .vector_table:

08000000 <_svector_table>:
 8000000:       20004ff8        strdcs  r4, [r0], -r8 ; initial Stack Pointer

08000004 <cortex_m_rt::RESET_VECTOR>:
 8000004:       08000131        stmdaeq r0, {r0, r4, r5, r8}

08000008 <EXCEPTIONS>:
 8000008:       080001bd        stmdaeq r0, {r0, r2, r3, r4, r5, r7, r8}
 (..)

Disassembly of section .stack:

20000000 <.stack>:
        ...

Disassembly of section .bss:

20004ff8 <cortex_m_quickstart::main::X>:
20004ff8:       00000000        andeq   r0, r0, r0

Disassembly of section .data:

20004ffc <_sdata>:
20004ffc:       00000001        andeq   r0, r0, r1

closes #34

TODO

  • support a .heap region
  • update documentation
  • publish swap-ld cortex-m-rt-ld

@japaric
Copy link
Member Author

japaric commented Nov 22, 2017

Now with .heap support.

Without swap-ld and with _heap_size = 0x1000.

$ arm-none-eabi-size -Ax app
section                                                                   size         addr
.vector_table                                                            0x400    0x8000000
.text                                                                     0x94    0x8000400
.rodata                                                                    0x0    0x8000494
.stack                                                                  0x5000   0x20000000
.bss                                                                       0x4   0x20000000
.data                                                                      0x4   0x20000004
.heap                                                                   0x1000   0x20000008

And with swap-ld:

$ arm-none-eabi-size -Ax app
section                                                                   size         addr
.vector_table                                                            0x400    0x8000000
.text                                                                     0x94    0x8000400
.rodata                                                                    0x0    0x8000494
.stack                                                                   0x3ff8   0x20000000
.bss                                                                       0x4   0x20003ff8
.data                                                                      0x4   0x20003ffc
.heap                                                                   0x1000   0x20004000

@japaric
Copy link
Member Author

japaric commented Nov 25, 2017

r? @whitequark

Thoughts on the required changes to implemented this? You expressed concerns about implementing stack overflow protection using this method so I want to know if you find the changes too invasive or complex.

Basically, this PR:

  • Made _sbss overridable but the symbol is not documented as being overridable as that's an implementation detail of swap-ld.
  • Added a configurable _heap_size symbol, that defaults to 0. This defines the size of the .heap section (see below).
  • Created ficticious .stack and .heap sections that represent the RAM regions destined to the stack and the heap. These come in handy when looking at size -Ax because they show the size of the stack and the heap. The size of .stack is totally wrong (it reports 0) when the stack is placed in a second RAM region, though, because we don't know the name of the region (it could be CCRAM, or RAM2, or something else) so we can't use ORIGIN or LENGTH to figure out its size.

@whitequark
Copy link
Contributor

LGTM on the basic concept. I see no better way.

NAK on the name of the linker, just call it cortex-m-rt-ld or something like that, swap-ld is uninformative at best and may invoke visions of paging at worst. And it's not even swapping anything when the stack is in another memory region.

@japaric
Copy link
Member Author

japaric commented Nov 27, 2017

@whitequark Thanks for the feedback. Agree on renaming the linker wrapper.

This is one possible solution to the stack overflow problem described in #34. This approach uses a
linker wrapper, called [swap-ld], to generate the desired memory layout. See #34 for a description
of the desired memory layout and #41 for a description of how `swap-ld` works.

The observable effects of this change in cortex-m programs are:

- the `_sbss` symbol is now override-able.
- there is now a `.stack` linker section that denotes the span of the call stack. `.stack` won't be
  loaded into the program; it just exists for informative purposes (`swap-ld` uses this
  information).

Given the following program:

``` rust
fn main() {
    static mut X: u32 = 0;
    static mut Y: u32 = 1;

    loop {
        unsafe {
            ptr::write_volatile(&mut X, X + 1);
            ptr::write_volatile(&mut Y, Y + 1);
        }
    }
}
```

If you link this program using the `arm-none-eabi-ld` linker, which is the cortex-m-quickstart
default, you'll get the following memory layout:

``` console
$ console
section                                                                   size         addr
.vector_table                                                            0x130    0x8000000
.text                                                                     0x94    0x8000130
.rodata                                                                    0x0    0x80001c4
.stack                                                                  0x5000   0x20000000
.bss                                                                       0x4   0x20000000
.data                                                                      0x4   0x20000004
```

Note how the space reserved for the stack (depicted by the `.stack` linker section) overlaps with
the space where .bss and .data reside.

If you, instead, link this program using `swap-ld` you'll get the following memory layout:

``` console
$ arm-none-eabi-size -Ax app
section                                                                   size         addr
.vector_table                                                            0x130    0x8000000
.text                                                                     0x94    0x8000130
.rodata                                                                    0x0    0x80001c4
.stack                                                                  0x4ff8   0x20000000
.bss                                                                       0x4   0x20004ff8
.data                                                                      0x4   0x20004ffc
```

Note that no overlap exists in this case and that the call stack size has reduced to accommodate the
.bss and .data sections.

Unlike #41 the addresses of static variables is now correct:

``` console
$ arm-none-eabi-objdump -CD app
Disassembly of section .vector_table:

08000000 <_svector_table>:
 8000000:       20004ff8        strdcs  r4, [r0], -r8 ; initial Stack Pointer

08000004 <cortex_m_rt::RESET_VECTOR>:
 8000004:       08000131        stmdaeq r0, {r0, r4, r5, r8}

08000008 <EXCEPTIONS>:
 8000008:       080001bd        stmdaeq r0, {r0, r2, r3, r4, r5, r7, r8}
 (..)

Disassembly of section .stack:

20000000 <.stack>:
        ...

Disassembly of section .bss:

20004ff8 <cortex_m_quickstart::main::X>:
20004ff8:       00000000        andeq   r0, r0, r0

Disassembly of section .data:

20004ffc <_sdata>:
20004ffc:       00000001        andeq   r0, r0, r1
```

closes #34

[swap-ld]: https://github.com/japaric/swap-ld
This adds a fictitious `.heap` linker section that denotes the span of a heap region. The size of
this linker section is governed by the `_heap_size` symbol which defaults to 0 but can be overridden
by the user.
also fix a bug when .bss was empty but .data was not
@japaric japaric changed the title [WIP] Zero cost stack overflow protection, take 2 Zero cost stack overflow protection, take 2 Feb 17, 2018
@japaric japaric merged commit eb9970f into master Feb 17, 2018
@japaric japaric deleted the swap-ld branch February 17, 2018 16:19
japaric added a commit that referenced this pull request Apr 6, 2018
this commit adds LLD support by removing all INFO sections. LLD kind of supports INFO in the form of
NOLOAD but when the linker script contains NOLOAD sections LLD emits a binary with false `size`
information: for example, it reported a fake increase of 20KB in .text and a fake increase of 1KB in
.bss when compiling a program that only allocates a single Box.

As the INFO sections are gone we can no longer support the stack overflow protection added in #43 so
all the other related changes, like making _stack_start overridable, have been removed as well.
If you want to continue using stack overflow protection you can stick to v0.3.x.

As the .debug_gdb_scripts output section has been removed from the linker script these changes will
only reliably support both LD and LLD if/when rust-lang/rust#49728 lands.

closes #53
japaric added a commit that referenced this pull request Apr 8, 2018
this commit adds LLD support by removing all INFO sections. LLD kind of supports INFO in the form of
NOLOAD but when the linker script contains NOLOAD sections LLD emits a binary with false `size`
information: for example, it reported a fake increase of 20KB in .text and a fake increase of 1KB in
.bss when compiling a program that only allocates a single Box.

As the INFO sections are gone we can no longer support the stack overflow protection added in #43 so
all the other related changes, like making _stack_start overridable, have been removed as well.
If you want to continue using stack overflow protection you can stick to v0.3.x.

As the .debug_gdb_scripts output section has been removed from the linker script these changes will
only reliably support both LD and LLD if/when rust-lang/rust#49728 lands.

closes #53
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(Zero cost) stack overflow protection
2 participants