gcc Function Attributes alloc_size

ddatsh

dev #redis redis

基于 11965 里面的 示例代码

gcc 12 +

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

struct a {
    char c;
    char buf[];
};

int main() {
    const int BUFSIZE = 5;

    struct a *a = malloc(sizeof(*a) + BUFSIZE);
    printf("sizeof(struct a)=%zu \n", sizeof(struct a));
    printf("sizeof(*a)=%zu \n", sizeof(*a));
    size_t n = malloc_usable_size(a);
    printf("a usable size=%zu \n", n);

    // Dummy condition to prevent compiler throw warning on compile time
    // n will always be n >= BUFSIZE
    size_t copy = n >= BUFSIZE ? BUFSIZE + 3 : BUFSIZE;
    memcpy(a->buf, "12345678", copy);
    printf("%.*s \n", (int) copy, (char*) a->buf);

    return 0;
}
gcc main.c
./a.out

“正常” 运行了

sizeof(struct a)=1
sizeof(*a)=1
a usable size=24
12345678

ida反编译

上点强度就报错了

gcc -O2 -Wall -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3  main.c
或者直接 gcc -O2也一样
sizeof(struct a)=1
sizeof(*a)=1
a usable size=24
*** buffer overflow detected ***: terminated
Aborted (core dumped)

仿 redis 之 attribute alloc_size

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

struct a {
    char c;
    char buf[];
};
#define UNUSED(x) ((void)(x))

__attribute__((alloc_size(2), noinline)) void *extend_to_usable(void *ptr, size_t size) {
    UNUSED(size);
    return ptr;
}

int main() {
    const int BUFSIZE = 5;

    struct a *a = malloc(sizeof(*a) + BUFSIZE);
    printf("sizeof(struct a):%zu \n", sizeof(struct a));
    printf("sizeof(*a):%zu \n", sizeof(*a));
    size_t n = malloc_usable_size(a);
    printf("a usable size: %zu \n", n);

    // Dummy condition to prevent compiler throw warning on compile time
    // n will always be n >= BUFSIZE
    size_t copy = n >= BUFSIZE ? BUFSIZE + 3 : BUFSIZE;
    a=extend_to_usable(a, n);
    memcpy(a->buf, "12345678", copy);
    printf("%.*s \n", (int) copy, (char*) a->buf);

    return 0;
}
#define UNUSED(x) ((void)(x))

此宏可以强制的产生一个对该变量的使用操作, 从而消除编译器产生的”unused variable”警告

malloc_size

malloc内存分配时的内存对齐,实际分配的内存空间大小与申请的空间大小可能并不一致, 可能比申请的空间大。得知具体的空间大小后, 在 Redis 中, SDS 库借助于此机制调整 alloc 变量的取值, 从而减少不必要的内存分配

extend_to_usable是一个纯声明性质的函数, 通过alloc_size(2)属性标记, 向编译器表明这个函数的第二个参数是 ptr 执行的内存空间的实际大小

此函数通常与malloc_size函数配合使用, 通过malloc_size函数获取实际分配的内存空间大小后, 使用extend_to_usable函数向编译器声明后续代码会按照实际分配的空间大小使用

FORTIFY_SOURCE