C语言程序在使用GDB调试时无法获取文件描述符

5
我不是专业的C程序员。我在使用GDB调试程序时遇到了问题。(我要修复的错误与我在此处提问的问题无关。)我的问题是,当我直接从shell运行二进制文件时,程序运行得很好,但当我使用GDB运行它时,程序崩溃了。
以下是一些有用的程序信息:这是一个20多年前为Solaris编写的数据库软件,在Linux上进行了移植,它是setuid(但不是root权限,谢天谢地)。
当程序试图打开一个文件进行写操作时,它会在GDB中崩溃。通过使用GDB,我能够确定崩溃发生的原因是下面的系统调用失败了:
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);

为了澄清: path 是指一个不存在的锁文件路径。如果该锁文件存在,则程序在调用这个系统调用之前就会干净地关闭。
我不明白为什么这个系统调用会失败,因为 1)运行此程序的用户对包含path的目录具有rwx权限(我已通过检查存储在path中的变量的值进行验证),2)当我没有使用GDB进行调试时,程序成功地打开要写入的文件。
是否有任何原因我不能做到呢?

当open()失败时,errno是什么? - ensc
你正在进入开放系统调用吗? - Jamil Seaidoun
@ensc - 返回值为“-1”。你知道这意味着什么吗?http://codewiki.wikidot.com/c:system-calls:open在这方面没有帮助。@steveha - 这是一个好建议,但我在程序尝试打开文件的那一行暂停了程序,而那个文件不存在。 - jayhendren
1
如果您使用调试器停止程序并更改其内部数据,然后恢复它,您可以打败程序的内部安全措施。为了避免这种情况,内核在启用调试时禁用setuid-ness。 (考虑一个简单的例子,一个带有“高分”文件的游戏。您在调试器下运行游戏,立即死亡,但停在“注册得分”函数处。您将自己的得分更改为十亿分!并恢复。) - torek
1
@torek - 这就是问题所在,谢谢。我使用 sudo su - 切换到了这个程序运行的用户,现在 GDB 可以工作了。请将其作为答案添加,我会接受它。 - jayhendren
显示剩余21条评论
1个回答

8
关键在于这段话:

... 是 setuid 的(但不是 root,感谢上帝)。

当你在任何调试器下运行程序(使用任何停止-检查/修改程序功能),内核会禁用 setuid 权限,即使是非 root 的 setuid。
如果你仔细想一想,这是有道理的。考虑一个保存“最高分”文件并使用“setuid games”来完成此操作的游戏,就可以明白了。
fd = open(GAME_SCORE_FILE, open_mode, file_mode);
score_data = read_scores(fd);
/* set breakpoint here or so */
if (check_for_new_high_score(current_score, score_data)) {
    printf("congratulations, you've entered the High Scores records!\n");
    save_scores(fd, score_data);
}
close(fd);

"高分数"文件的访问受文件权限保护:只有"games"用户才能写入。

如果你在调试器下运行游戏,你可以在标记行处设置断点,将current_score数据设置为超级高的值,然后恢复程序。

为了避免允许调试器破坏setuid程序的内部数据,内核会在启用调试工具时简单地禁用setuid-ness。如果你可以通过su(或sudo等)切换到该用户,表示你已经获得了权限,而不考虑任何调试,那么你可以以该用户身份运行gdb本身,从而使程序作为它“本应该”设置的setuid用户运行。


不幸的是,这意味着我需要找到一种新的调试方法来解决我的问题。因为我正在使用GDB调试的错误只发生在程序的一个用户身上,所以重要的是我必须以她的用户身份运行此程序。感谢您的帮助! - jayhendren
数字。 :-) setuid 很麻烦,难怪他们从 Plan 9 中放弃了整个概念(具有适当身份验证的客户端/服务器是一个更简单的模型)。查看她的设置时要注意的第一件事是任何特殊的环境设置,例如不同的环境变量、用户限制 (ulimit 输出),有时甚至包括类似终端模式之类的奇怪东西。 - torek
我认为问题可能出在她的Windows PuTTY客户端上。她遇到了奇怪的按键绑定问题。在Linux环境中,包括我自己,都无法复制这个问题,而且当我以su -身份登录到她的账户时,我也无法复制它。我希望能从她的Windows电脑上作为她来运行GDB,并检查存储她按下的键的变量的值,但恐怕这种方法行不通。 - jayhendren
@jayhendren,目前你最好的选择是在代码中添加日志记录语句,然后让她运行它并查看日志输出。当一切都失败时,可以使用printf()进行调试。 - steveha
我在想,通过附加到别人正在运行的程序实例上,我可以让gdb正常工作。首先,她会启动程序,然后我会使用sugdb <program name> -p <pid>。我已经尝试过一些方法,但是我无法正确地使其工作。在附加到进程后,GDB不断报告“ptrace:操作不允许”。 - jayhendren
修复了。使用 sudo gdb <程序名称> -p <pid> 成功了。太好了! - jayhendren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接