HugoNikanors blogg‽

A blog about nothing, but mostly itself.

Hugo Hörnquist 15 Nov 2022
Lästid: 3 minut(er)

When C Looks Like Java

A while ago on r/programminghorror someone posted this beautiful code snippet:

private static int stringSize(String s){
    int size = 0;
    for (int i = 0; i < s.length(); i++) {
        size++;
    }
    return size;
}

It was initially tagged C, which someone commented on.

Why is this tagged as C? Pretty sure this is Java or a similar language.

TheGhostOfInky

Which got the response (with the name removed since they removed their comment).

Its definitely legal c code, don't know what you're on about

[DELETED]

I however got slightly nerd-sniped by this. Could this be legal C code? Unfortunately not, since private isn't a function or storage-class specifier in C. But for arguments say lets assume someone has a null macro as some form of weird inline documentation.

#define private

This leaves us with s.length(). It's obviously legal C, assuming that String is a typedef:ed struct containing a function pointer length, e.g.

typedef struct String {
    size_t (*length)(void);
    /* ... */
} String;

This however doesn't seem to help us. Length has no way to known who called it, since there is no this object in C. Or is there some way to find this information‽

The object s must reside on the call stack (since we access the method with a period[1]). So if we could access the call stack, and have some way of identifying the string object then we could just search the stack until we find it. The top of the stack could theoretically be accessed by taking the address of a stack allocated variable. C however officially doesn't have a stack between procedures, so instead I opt for the GNU extension __builtin_frame_address. I then place a magic value at the start of each String, and search for that value.

Is it stupid? Yes. Does it work? Barely? Should you do this? Maybe in C++.

Code Listing

Here's a complete and compilable code listing for the stuff described above.

It compiles with 0 warnings and errors, through gcc -std=gnu11 -Wall -O0 main.c, but once we increse the optimization level it segfaults.

#include <stddef.h>
#include <string.h>
#include <stdio.h>

#define private

/* Magic cookie used to find strings on stack */
#define COOKIE 0xA1891EE4C9E90839

typedef struct String {
    unsigned long long cookie;
    size_t (*length)(void);
    char *data;
} String;

/* Find our magic cookie on stack, and return that string */
String *find_string(void) {
    /* __builtin_frame_address is a GNU extension. This is tested on
     * x86_64 Linux, and will most likely break elsewhere */
    for (char *sp = __builtin_frame_address(0);; ++sp) {
        if (*((unsigned long long *) sp) == COOKIE) {
            return (String*) sp;
        }
    }
}

size_t string_length(void) {
    return strlen(find_string()->data);
}

String make_string(char *data) {
    return ((String){
        .length = &string_length,
        .data = data,
        .cookie = COOKIE,
    });
}

private static int stringSize(String s) {
    int size = 0;
    for (int i = 0; i < s.length(); i++) {
        size++;
    }
    return size;
}

int main() {
    String s = make_string("Hello, World!");
    printf("%d\n", stringSize(s));
}

Footnotes

  1. role="doc-endnote" id="footnote1">

    It could techically be passed by register. It however probably isn't since the structure looks rather heavy (containing its own vtable).


RSS RSS-feed to see more like this.
About Contact Legal Q&A