不止一次在网上看到一篇名为《12个有趣的C语言问答》的博文被郑重其事地转来转去( google了一下,竟然有154,000条结果,其中不乏一些知名的技术网站),感到很是滑稽。由于那明摆着是一篇垃圾文,质量低下,漏洞比比皆是。其中基本上没有多少技术养分,却是有不少技术毒素。html
这篇垃圾文被转反复载的缘由可能有两个:一是标题取的好,其中有“有趣”二字,很多很傻很天真的人就觉得真的颇有趣;第二个缘由多是这是一篇翻译文章,原文为12 Interesting C Interview Questions and Answers,有些人潜意识里可能觉得外文的东西会颇有技术含量。但实际上洋文中也有垃圾,洋人中也有不少外行,正如国外也有老谭《C语言程序设计》那种门外汉写得畅销垃圾书(譬如邮电社翻译的《写给你们看的C语言书》,参见劣质代码评析——《写给你们看的C语言书(第2版)》附录B之21点程序(一) )同样。对国外的东西一样不能盲从轻信,不能根据畅销程度或转发多少更不能仅仅根据其名字来判断技术价值。数组
下面对这篇《12个有趣的C语言问答》垃圾文,参照其出处(由于翻译本有不少错误),简要地评析一下。但愿对垃圾文的不断扩散多少能起到点遏制的做用。 promise
0. gets() 方法
Q:如下代码有个被隐藏住的问题,你能找到它吗? domint main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets(buff); printf("\n The buffer entered is [%s]\n",buff); return 0; }A:这个不显眼的问题就是使用了 gets() 方法。此方法接受一个string类型参数,可是却没有检测此数值是否 有足够的空间来拷贝数据。因此这里咱们通常用 fgets() 方法未来的更好。函数
Answer: The hidden problem with the code above is the use of the function gets(). This function accepts a string from stdin without checking the capacity of buffer in which it copies the value. This may well result in buffer overflow. The standard function fgets() is advisable to use in these cases.oop
翻译很成问题。根据原文,是“gets()函数”,不是“gets()方法”;接受一个string参数,不是“string类型参数”(C语言中根本没有这种类型)。其他部分的翻译也有问题,但对原意影响不大,就很少说了。 post
Answer中说使用gets()函数可能致使buffer的overflow,这一点没什么疑问。由于这个缘故,C语言如今已经废弃了gets()函数。
问题在于代码中的网站
memset(buff,0,sizeof(buff));
这句,这句很无聊得很愚蠢。它的效果是在buff中填充0,但其实根本用不着这样调用函数来实现,只须要简单地this
char buff[10] = { '\0' };
就足够了。更重要的是,从后面对buff的使用来看,根本不必在buff中填充0。 google
1,strcpy() 方法
Q:
密码防御是很基本的功能,看看可否搞定下面这段代码#include<stdio.h> int main(int argc, char *argv[]) { int flag = 0; char passwd[10]; memset(passwd,0,sizeof(passwd)); strcpy(passwd, argv[1]); if(0 == strcmp("LinuxGeek", passwd)) { flag = 1; } if(flag) { printf("\n Password cracked \n"); } else { printf("\n Incorrect passwd \n"); } return 0; }
晕!Answer压根没翻译。那么多转来转去的人竟然对此视而不见! 从这里就不难看出哪些转这篇垃圾的人究竟有没有认真看,究竟有没有本身的头脑。这也一样可以解释,为何垃圾能传播很广,以及为何那些说谭浩强的书发行量大就必定好的见解是无脑人的看法。
根据原文,解答是这样的:
Answer: Yes. The authentication logic in above password protector code can be compromised by exploiting the loophole of strcpy() function. This function copies the password supplied by user to the ‘passwd’ buffer without checking whether the length of password supplied can be accommodated by the ‘passwd’ buffer or not. So if a user supplies a random password of such a length that causes buffer overflow and overwrites the memory location containing the default value ’0′ of the ‘flag’ variable then even if the password matching condition fails, the check of flag being non-zero becomes true and hence the password protection is breached.
For example :
$ ./psswd aaaaaaaaaaaaa
Password cracked
So you can see that though the password supplied in the above example is not correct but still it breached the password security through buffer overflow.
To avoid these kind of problems the function strncpy() should be used.
Note from author : These days the compilers internally detect the possibility of stack smashing and so they store variables on stack in such a way that stack smashing becomes very difficult. In my case also, the gcc does this by default so I had to use the the compile option ‘-fno-stack-protector’ to reproduce the above scenario.
不翻译了。这个解答的意思就是经过输入较长的argv[1],借助数组越界来改变flag,实现“break”这个程序的目的(Can you break it without knowing the password)。因此应该使用strncpy()。
这种“break”多少有点歪门邪道的意味,并且做者也提到了,有些编译器能够防止这种状况,因此这种方法其实意义不大。
这里想说的依然是代码风格的问题——那个flag极其丑陋,不只丑陋,没有必要存在,还进行了没必要要的初始化。那个
一样多此一举。
甚至连passwd这个数组都不必,只要直接比较"LinuxGeek"和argv[1]指向的字符串就能够了。代码能够这样写:
#include<stdio.h> int main(int argc, char *argv[]) { //char passwd[10]; //strncpy(passwd, argv[1], 10); if( strcmp("LinuxGeek", argv[1] ) == 0 ) //if( strcmp("LinuxGeek", passwd) == 0 ) { printf("\n Password cracked \n"); } else { printf("\n Incorrect passwd \n"); } return 0; }
简洁又天然。
(未完待续)