我正在为编程比赛开发一个在线评测系统。由于某些编程比赛不允许使用C/C++内联汇编,因此我希望在我的系统中添加相同的限制。
我想让GCC在编译包含内联汇编的C/C++程序时产生错误,以便拒绝任何包含内联汇编的程序。有没有办法实现这一点?
注意:禁用内联汇编只是遵守规则,而不是出于安全考虑。
我正在为编程比赛开发一个在线评测系统。由于某些编程比赛不允许使用C/C++内联汇编,因此我希望在我的系统中添加相同的限制。
我想让GCC在编译包含内联汇编的C/C++程序时产生错误,以便拒绝任何包含内联汇编的程序。有没有办法实现这一点?
注意:禁用内联汇编只是遵守规则,而不是出于安全考虑。
是的,有几种方法;但这些方法并不适用于安全性,只是防护栏杆,可以故意绕过,但会阻止人们在不知道不应该使用asm
的地方意外使用。
asm
关键字(仅限C语言)要在编译阶段执行此操作,请使用参数-fno-asm
。但请注意,这只会影响C语言中的asm
,而不是C ++。也不会影响任何一种语言的__asm__
或__asm
。
文档:
-fno-asm
不将"
asm
"、"inline
"或"typeof
"识别为关键字,以便代码可以使用这些单词作为标识符。您可以使用关键字"__asm__
"、"__inline__
"和"__typeof__
"代替。-ansi
意味着-fno-asm
。在C ++中,此开关仅影响"
typeof
"关键字,因为"asm
"和"inline
"是标准关键字。您可能想使用-fno-gnu-keywords
标志,其具有相同的效果。在C99模式下(-std=c99
或-std=gnu99
),此开关仅影响"asm
"和"typeof
"关键字,因为"inline
"是ISO C99中的标准关键字。
您可以使用参数-Dasm=error -D__asm__=error -D__asm=error
请注意,这个结构是通用的。它的作用类似于#define
。文档说:
所以它的作用就是简单地将
-D name=definition
定义的内容将被分词并处理,就好像它们出现在# define指令的翻译阶段三中一样。特别是,定义将被嵌入的换行符截断。
...
asm
、__asm
或__asm__
的出现改为error
。这是在预处理阶段完成的。您不必使用error
,只需选择任何无法编译的内容即可。
-D'asm(...)=_Static_assert(0,"inline assembly not allowed")'
。如果存在名为error
的标识符,这也将解决该问题。
注意:此方法需要-std=c11
或更高版本。
grep
:grep -nr "asm"
__asm__
,但可能会产生误报,例如如果您有包含子字符串"asm"
的字符串文字、标识符或注释。但在您的情况下,您可以通过禁止源代码中任何位置出现该字符串来解决此问题。只需更改规则。
请注意,禁用汇编可能会导致其他问题。例如,我无法使用此选项的stdio.h
。通常,系统头文件包含内联汇编代码。
除了微不足道的#undef __asm__
之外,还可以将字符串作为机器代码执行。请参见以下答案中的示例:https://dev59.com/3WMl5IYBdhLWcg3wV10F#18477070
链接中的代码片段:
/* our machine code */
char code[] = {0x55,0x48,0x89,0xe5,0x89,0x7d,0xfc,0x48,
0x89,0x75,0xf0,0xb8,0x2a,0x00,0x00,0x00,0xc9,0xc3,0x00};
/* copy code to executable buffer */
void *buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANON,-1,0);
memcpy (buf, code, sizeof(code));
/* run code */
int i = ((int (*) (void))buf)();
mmap
/ memcpy
,只需使用 const char code[] = { ... };
静态存储类和 const
,它可以放在 .rodata
中,作为可执行文件的 Text 段的一部分链接,因此是可执行的。 - Peter Cordesmmap
或mprotect
来创建可执行页面。你仍然可以通过一个包装器宏来阻止mmap
中的PROT_EXEC
标志。但是只读数据已经在可执行页面中,这打开了大门,使得如果人们将其隐藏在void*
变量或static inline
函数之后,就没有可靠的方法来检测将数据转换为函数指针。 - Peter Cordes
asm
,__asm__
和__asm
。如果这是为了阻止有动机的人使用汇编语言,而不是意外预防,那么有人可以使用CPP宏将__as
和m__
粘贴在一起,以使__asm__
出现在预处理器输出中。我认为你不能用CPP来防御特定的标记。 - Peter Cordes#undef __asm__
是打败#define
或-D
选项的最简单方法。正如klutt的答案所示,未来的读者不应该依赖此方法来保证安全性,只能防止无意识的绕过以保持诚实用户的诚实。如果您允许人们编译并运行C代码,则应假定它可以执行任意机器代码;在系统调用级别上实现安全性是您应该努力的方向。 - Peter Cordes