3 多线程

3.1 【必须】变量应确保线程安全性

当一个变量可能被多个线程使用时,应当使用原子操作或加锁操作。

  1. // Bad
  2. char g_somechar;
  3. void foo_thread1() {
  4. g_somechar += 3;
  5. }
  6. void foo_thread2() {
  7. g_somechar += 1;
  8. }

对于可以使用原子操作的,应当使用一些可以确保内存安全的操作,如:

  1. // Good
  2. volatile char g_somechar;
  3. void foo_thread1() {
  4. __sync_fetch_and_add(&g_somechar, 3);
  5. }
  6. void foo_thread2() {
  7. __sync_fetch_and_add(&g_somechar, 1);
  8. }

对于 C 代码,C11 后推荐使用 atomic 标准库。 对于 C++代码,C++11 后,推荐使用 std::atomic

关联漏洞:

高风险-内存破坏

中风险-逻辑问题

3.2 【必须】注意signal handler导致的条件竞争

竞争条件经常出现在信号处理程序中,因为信号处理程序支持异步操作。攻击者能够利用信号处理程序争用条件导致软件状态损坏,从而可能导致拒绝服务甚至代码执行。

  1. 当信号处理程序中发生不可重入函数或状态敏感操作时,就会出现这些问题。因为信号处理程序中随时可以被调用。比如,当在信号处理程序中调用free时,通常会出现另一个信号争用条件,从而导致双重释放。即使给定指针在释放后设置为NULL,在释放内存和将指针设置为NULL之间仍然存在竞争的可能。
  2. 为多个信号设置了相同的信号处理程序,这尤其有问题——因为这意味着信号处理程序本身可能会重新进入。例如,malloc()和free()是不可重入的,因为它们可能使用全局或静态数据结构来管理内存,并且它们被syslog()等看似无害的函数间接使用;这些函数可能会导致内存损坏和代码执行。
  1. // Bad
  2. char *log_message;
  3. void Handler(int signum) {
  4. syslog(LOG_NOTICE, "%s\n", log_m_essage);
  5. free(log_message);
  6. sleep(10);
  7. exit(0);
  8. }
  9. int main (int argc, char* argv[]) {
  10. log_message = strdup(argv[1]);
  11. signal(SIGHUP, Handler);
  12. signal(SIGTERM, Handler);
  13. sleep(10);
  14. }

可以借由下列操作规避问题:

  1. 避免在多个处理函数中共享某些变量。
  2. 在信号处理程序中使用同步操作。
  3. 屏蔽不相关的信号,从而提供原子性。
  4. 避免在信号处理函数中调用不满足异步信号安全的函数。

关联漏洞:

高风险-内存破坏

中风险-逻辑问题

3.3 【建议】注意Time-of-check Time-of-use (TOCTOU) 条件竞争

TOCTOU: 软件在使用某个资源之前检查该资源的状态,但是该资源的状态可以在检查和使用之间更改,从而使检查结果无效。当资源处于这种意外状态时,这可能会导致软件执行错误操作。

当攻击者可以影响检查和使用之间的资源状态时,此问题可能与安全相关。这可能发生在共享资源(如文件、内存,甚至多线程程序中的变量)上。在编程时需要注意避免出现TOCTOU问题。

例如,下面的例子中,该文件可能已经在检查和lstat之间进行了更新,特别是因为printf有延迟。

  1. struct stat *st;
  2. lstat("...", st);
  3. printf("foo");
  4. if (st->st_mtimespec == ...) {
  5. printf("Now updating things\n");
  6. UpdateThings();
  7. }

TOCTOU难以修复,但是有以下缓解方案:

  1. 限制对来自多个进程的文件的交叉操作。
  2. 如果必须在多个进程或线程之间共享对资源的访问,那么请尝试限制”检查“(CHECK)和”使用“(USE)资源之间的时间量,使他们相距尽量不要太远。这不会从根本上解决问题,但可能会使攻击更难成功。
  3. 在Use调用之后重新检查资源,以验证是否正确执行了操作。
  4. 确保一些环境锁定机制能够被用来有效保护资源。但要确保锁定是检查之前进行的,而不是在检查之后进行的,以便检查时的资源与使用时的资源相同。

关联漏洞:

高风险-内存破坏

中风险-逻辑问题