codecamp

C/C++ 多线程

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

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

// Bad
char  g_somechar;
void foo_thread1() {
  g_somechar += 3;
}


void foo_thread2() {
  g_somechar += 1;
}

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

// Good
volatile char g_somechar;
void foo_thread1() {
  __sync_fetch_and_add(&g_somechar, 3);
}


void foo_thread2() {
  __sync_fetch_and_add(&g_somechar, 1);
}

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

关联漏洞:

  • 高风险-内存破坏

  • 中风险-逻辑问题

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

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

  1. 当信号处理程序中发生不可重入函数或状态敏感操作时,就会出现这些问题。因为信号处理程序中随时可以被调用。比如,当在信号处理程序中调用free时,通常会出现另一个信号争用条件,从而导致双重释放。即使给定指针在释放后设置为NULL,在释放内存和将指针设置为NULL之间仍然存在竞争的可能。
  2. 为多个信号设置了相同的信号处理程序,这尤其有问题——因为这意味着信号处理程序本身可能会重新进入。例如,malloc()和free()是不可重入的,因为它们可能使用全局或静态数据结构来管理内存,并且它们被syslog()等看似无害的函数间接使用;这些函数可能会导致内存损坏和代码执行。

// Bad
char *log_message;


void Handler(int signum) {
  syslog(LOG_NOTICE, "%s\n", log_m_essage);
  free(log_message);
  sleep(10);
  exit(0);
}


int main (int argc, char* argv[]) {
  log_message = strdup(argv[1]);
  signal(SIGHUP, Handler);
  signal(SIGTERM, Handler);
  sleep(10);
}

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

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

关联漏洞:

  • 高风险-内存破坏

  • 中风险-逻辑问题

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

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

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

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

struct stat *st;


lstat("...", st);


printf("foo");


if (st->st_mtimespec == ...) {
  printf("Now updating things\n");
  UpdateThings();
}

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

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

关联漏洞:

  • 高风险-内存破坏

  • 中风险-逻辑问题
C/C++ 不推荐的编程习惯
C/C++ 加密解密
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }