我很想知道是否有可能在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);
}
这种方法增加了太多的开销以包含在现有代码中(增加了很多噪音并且很难维护),所以我想知道是否有其他方法告诉编译器变量未初始化。
我知道有静态检查器,我确实使用了这些,但是我正在寻找可以在编译时发出警告并且没有误报的东西,我相信在这种情况下可以避免某种类型的错误。
答案 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