Skip to content

TLS memory errors with aligned thread-local variables #497

@Meziu

Description

@Meziu

Born from: rust3ds/ctru-rs#60

Description

After some testing on our Rust libraries for this system, we've noticed a weird bug with the use of thread-local storage. With some investigation, we have pinpointed the problem to the use of aligned thread-local variables. As our tests show, the offset of the variable pointers are wrong when one or more aligned TLS variables exist, causing memory leaks, access of uninitialized memory etc. This issue appears in both Rust and C programs.

Furthermore, this issue seems to only affect (at least in our examples) programs with a debug optimization level.

E.g. :
debug mode in Rust reproduces the bug.
release mode in Rust doesn't reproduce the bug.
-Og in C reproduces the bug.
-O2 in C doesn't reproduce the bug.

Here is a test program to try the issue. Remember to use -Og to replicate:

#include <3ds.h>
#include <stdio.h>
#include <string.h>

typedef ALIGN(4) struct {
    u8 inner[3];
} Align4;

typedef ALIGN(16) struct {
    u8 inner[3];
} Align16;

static __thread Align4 BUF_4 = {.inner = {2, 2, 2}};
static __thread Align16 BUF_16 = {.inner = {1, 1, 1}};

int
main(int argc, char** argv)
{
    gfxInitDefault();
    consoleInit(GFX_TOP, NULL);

    BUF_16.inner[0] = 0;

    bool reproduced = false;

    // Check if at least one byte has been offset improperly
    printf("[");
    for (int i = 0; i < 3; i++) {
        if (BUF_4.inner[i] != 2) {
            reproduced = true;
        }
        printf("%d, ", BUF_4.inner[i]);
    }
    printf("]\n");

    if (reproduced) {
        printf("Bug has been reproduced!\n");
    }
    else {
        printf("There has been no issue!");
    }

    // Main loop
    while (aptMainLoop()) {
        gspWaitForVBlank();
        hidScanInput();

        u32 kDown = hidKeysDown();
        if (kDown & KEY_START)
            break;  // break in order to return to hbmenu

        // Flush and swap framebuffers
        gfxFlushBuffers();
        gfxSwapBuffers();
    }

    gfxExit();
    return 0;
}

Notes about our Rust toolchain

Our Rust programs mainly use libctru (to substitute missing newlib functions), and the arm-none-eabi-gcc linker. Most of the linking flags are the same (basically those in a normal Makefile but in the Rust target-specification). Either way, it's hard to believe this one to be an issue in either of the compilers (rustc and gcc) as the issue reproduces in both. It's likely an issue with libctru or the linker.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions