有没有办法在GCC / Clang中将变量设置为未初始化?

时间:2013-05-25 02:59:32

标签: c gcc clang

我很想知道是否有可能在C中显式污染变量,因为它是未初始化的。

伪代码......

{
    int *array;
    array = some_alloc();
    b = array[0];
    some_free(array);
    TAINT_MACRO(array);

    /* the compiler should raise an uninitialized warning here */
    b = array[0];
}

以下是一种污染变量的方法示例,但是当'a'被分配了未初始化的var而不是第二次使用'a'时,GCC会发出警告。

{
    int a = 10;
    printf("first %d\n", a);
    do {
        int b;
        a = b;
    } while(0);
    printf("second %d\n", a);
}

我能想到的唯一解决方案是使用未初始化的变量显式地隐藏变量,(添加空洞以便没有未使用的警告)。

#define TAINT_MACRO_BEGIN(array) (void)(array); { void **array; (void)array;
#define TAINT_MACRO_END(array) } (void)(array);
{
    int *array;
    array = some_alloc();
    b = array[0];
    some_free(array);
    TAINT_MACRO_BEGIN(array);

    /* the compiler should raise an uninitialized warning here */
    b = array[0];
    TAINT_MACRO_END(array);
}

这种方法增加了太多的开销以包含在现有代码中(增加了很多噪音并且很难维护),所以我想知道是否有其他方法告诉编译器变量未初始化。

我知道有静态检查器,我确实使用了这些,但是我正在寻找可以在编译时发出警告并且没有误报的东西,我相信在这种情况下可以避免某种类型的错误。

3 个答案:

答案 0 :(得分:3)

我在海湾合作委员会名单上发了一个答案,但是因为我自己首先使用了这个......

在现代C和C ++中,我希望程序员使用有限的 可变范围来控制这种暴露。

例如,我认为你想要这样的东西(请注意 我正在使用的属性实际上并不存在,我只是想尝试 解释你的要求)。

int x = 1; // initialized 
int y;     // uninitialized 

x = y;     // use of uninitialized value 'y' 

y = 2;     // no longer uninitialized 
x = y;     // fine 

y = ((__attr__ uninitialized))0; // tell gcc it's uninitialized again 

x = y;    // warn here please. 

如果是这样,我会在C99(或更高版本)或C ++中使用其他范围(漂亮 确定它已经“在使用点声明”,因为至少在1993年的ARM ......):

int x = 1; // initialized 

{ 
    int y; // uninitialized 
    x = y; // warn here 
    y = 2; // ok, now it's initialized 
    x = y; // fine, no warning 
} 

{ 
    int y; // uninitialized again! 
    x = y; // warns here 
} 

额外的范围有点令人反感,但我很习惯他们 C ++(大量使用RAII技术。)

由于主流语言有答案,我不这样做 认为值得添加到编译器。

看看你的例子,你关注的是数组。那应该 与额外的范围一样工作,应该没有额外的 运行时成本,因为整个堆栈帧是在函数上分配的 进入(SFAIK,至少)。

答案 1 :(得分:2)

Based on an answer to a different question,您可以使用longjmp#define TAINT(x) \ do { \ static jmp_buf jb; \ if (setjmp(jb) == 0) { \ memset(&x, '\0', sizeof(x)); \ longjmp(jb, 1); \ } \ } while (0) 来更改本地变量,使其具有不确定的值。

x

如果TAINT是一个局部变量,那么在应用longjmp之后,它的值将在代码行中不确定。这是因为C.11§7.13.2¶3(强调我的):

  

所有可访问的对象都有值,以及所有其他组件   抽象机器具有状态,截至setjmp函数的时间   调用,除了自动存储对象的值   包含调用的函数的本地持续时间   没有volatile限定的相应setjmp宏   类型并在longjmp调用和// System.Fabric.FabricClient.PropertyManagementClient public Task<NameEnumerationResult> EnumerateSubNamesAsync(Uri name, NameEnumerationResult previousResult, bool recursive) 之间进行了更改   电话不确定

请注意,使用受到污染的变量不需要诊断。但是,编译器编写者正在积极地检测未定义的行为以增强优化,因此如果这仍然未被确定,我会感到惊讶。

答案 2 :(得分:1)

我会反过来,并在分配和释放函数周围包含污点宏。这就是我的想法:

#ifdef O_TAINT
volatile int taint_me;
#define TAINT(x, m) \
    if (taint_me) { goto taint_end_##x; } else {} x = m
#define free(x) free(x); taint_end_##x: (void)0
#else
#define TAINT(x, m) x = m
#endif

所以,你的例子看起来像这样:

int *array;
int b;

TAINT(array, malloc(sizeof(int)));
b = array[0];
printf("%d\n", b);
free(array);

/* the compiler should raise an uninitialized warning here */
b = array[0];
printf("%d\n", b);

这并不完美。每个受污染的变量只能调用一次free(),因为goto标签与变量名称相关联。如果跳转跳过其他初始化,您可能会得到其他误报。如果分配发生在一个函数中,并且内存在不同的函数中释放,则它不起作用。

但是,它提供了您为示例提出的行为。正常编译时,不会出现警告。如果使用-DO_TAINT进行编译,则会在b的第二个作业中显示警告。


我确实找到了一个相当通用的解决方案,但它涉及用开始/结束宏包围整个函数,并依赖于GCC扩展typeof运算符。解决方案最终看起来像这样:

void foo (int *array, char *buf)
{
    TAINT_BEGIN2(array, buf);
    int b;

    puts(buf);
    b = array[0];
    printf("%d\n", b);

    free(array);
    free(buf);

    /* the compiler should raise an uninitialized warning here */
    puts(buf);
    b = array[0];
    printf("%d\n", b);

    TAINT_END;
}

这里,TAINT_BEGIN2用于声明将获得污点处理的两个函数参数。不幸的是,这些宏有点乱,但很容易扩展:

#ifdef O_TAINT
volatile int taint_me;
#define TAINT(x, m) \
    if (taint_me) { goto taint_end_##x; } else {} x = m
#define TAINT1(x) \
    if (taint_me) { goto taint_end_##x; } else {} x = x##_taint
#define TAINT_BEGIN(v1) \
    typeof(v1) v1##_taint = v1; do { \
    typeof(v1##_taint) v1; TAINT1(v1)
#define TAINT_BEGIN2(v1, ...) \
    typeof(v1) v1##_taint = v1; TAINT_BEGIN(__VA_ARGS__); \
    typeof(v1##_taint) v1; TAINT1(v1)
#define TAINT_BEGIN3(v1, ...) \
    typeof(v1) v1##_taint = v1; TAINT_BEGIN2(__VA_ARGS__); \
    typeof(v1##_taint) v1; TAINT1(v1)
#define TAINT_END } while(0)
#define free(x) free(x); taint_end_##x: (void)0
#else
#define TAINT_BEGIN(x) (void)0
#define TAINT_BEGIN2(...) (void)0
#define TAINT_BEGIN3(...) (void)0
#define TAINT_END (void)0
#define TAINT1(x) (void)0
#define TAINT(x, m) x = m
#endif