我想知道是否有可能在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);
}这种方法增加了太多的开销,无法包含在现有代码中(增加了很多噪音,维护起来也很麻烦),所以我想知道是否有其他方法可以告诉编译器一个变量是未初始化的。
我知道有静态检查器,我确实使用了它们,但我正在寻找一些东西,它可以在编译时给出警告,并且没有误报,我相信在这种情况下是可能的,并且可以避免某种类型的bug。
发布于 2013-05-27 12:01:45
我在GCC的名单上发了一个答案,但因为我自己也是第一次使用…
在现代的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)。
发布于 2017-02-08 09:53:25
Based on an answer to a different question,您可以使用setjmp和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应用于它之后,它的值在代码行中将是不确定的。这是因为C.11§7.13.2Ó3(重点是我的):
除了自动存储持续时间的对象的值为indeterminate.之外,所有可访问的对象都具有值,并且抽象机的所有其他组件在调用longjmp函数时都具有状态,这些自动存储持续时间的对象对于包含相应setjmp宏的调用的函数是本地的,并且在setjmp调用和longjmp调用之间已被更改
请注意,使用被如此污染的变量不需要诊断。然而,编译器编写者正在积极地检测未定义的行为以增强优化,所以如果这永远不被诊断出来,我会感到惊讶。
发布于 2013-05-25 12:29:28
我会用另一种方式,在分配函数和空闲函数周围包装污点宏。这就是我的想法:
#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赋值时将出现警告。
我确实设计了一个相当通用的解决方案,但它涉及到使用begin/end宏将整个函数括起来,并依赖于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
#endifhttps://stackoverflow.com/questions/16745981
复制相似问题