Honggfuzz Linux arch_clone 源码阅读 (setjmp, clone)

Honggfuzz Linux arch_clone 源码阅读 (setjmp, clone)

阅读 Honggfuzz 系统架构相关源码,在创建子进程部分遇到了几个问题,经过研究得以解决,在此记录。

Source Code

代码节选自linux/arch.c,已添加注释,主要功能是以更细控制粒度(控制flags)创建子进程,而无参数的fork无法做到。

static uint8_t arch_clone_stack[128 * 1024] __attribute__((aligned(__BIGGEST_ALIGNMENT__)));
static __thread jmp_buf env;

HF_ATTR_NO_SANITIZE_ADDRESS
HF_ATTR_NO_SANITIZE_MEMORY
__attribute__((noreturn)) static int arch_cloneFunc(void* arg HF_ATTR_UNUSED) {
    longjmp(env, 1);
}

/* Avoid problem with caching of PID/TID in glibc */
static pid_t arch_clone(uintptr_t flags) {
    if (flags & CLONE_VM) {     // if child proc share the same VM with parent (change visible)
        LOG_E("Cannot use clone(flags & CLONE_VM)");
        return -1;
    }

    if (setjmp(env) == 0) {     // save the execution context
                                // return 0 if called directly, non-zero if indirectly (return from `longjmp`)
        void* stack_mid = &arch_clone_stack[sizeof(arch_clone_stack) / 2];
        /* Parent */
        return clone(arch_cloneFunc, stack_mid, flags, NULL, NULL, NULL);
    }
    /* Child */
    return 0;
}

Procedure

  1. arch_clone 检查flags中是否有CLONE_VM (父子进程共享同一个虚拟内存空间,相互影响),fuzz情景下不允许该方式
  2. setjmp 设置longjmp的checkpoint ,后续会介绍
  3. 如果在父进程中,就将预先分配的栈arch_clone_stack取中间点
  4. 执行glibc封装的 clone

Q & A

setjmp / longjmp

C标准库函数,用于执行"nonlocal gotos"(非本地跳转),对应goto语句的函数中跳转,longjmp允许跳转到调用栈上的任意位置,并恢复执行上下文。

通过setjmp(env)保存当前点的执行上下文信息(包括栈指针、寄存器值等)到jmp_buf类型参数env中,当执行longjmp(env, r),将恢复到env保存处的执行状态,并从那里开始执行。

具体保存的上下文内容如下所示:

setjmp

这种方法可以用于模拟C++ Exception,通过longjmp的返回值传递errno

注意:longjmp在非调用栈上的跳转行为未定义,如果执行setjmp保存上下文的函数已经返回,则不可使用longjmp恢复上下文

示例:

#include <stdio.h>
#include <setjmp.h>

jmp_buf buf;

void foo() {
	puts("In foo\n");
	longjmp(buf, 0);
	puts("foo returned normally");
	return;
}
int main() {
	
	printf("Hello world!\n");
	if (setjmp(buf) == 0) {
		printf("Start call functions!\n");
		foo();
	} else {
		puts("longjmp finish, return.");
	}
	return 0;
}
  • 程序在主函数保存了执行上下文,并跳转到foo函数执行
  • foo函数通过longjmp恢复执行流到main

过程如下,在执行setjmp前,各寄存器(主要关注栈)状态如下:

1

在执行longjmp前,各寄存器状态如下:

2

执行后,跳转到保存点,各寄存器的值也已经恢复:

在这里插入图片描述

clone stack参数

为什么需要在clone时附加stack参数呢?

  • 在使用CLONE_VM时,父子进程必不可以使用同一个栈,需要为子进程单独分配一个栈空间,这时候需要传入栈指针。

但是在这个场景下,没有使用CLONE_VM,也就不存在栈冲突的情况。这是因为代码使用了glibc封装的clone,stack参数会用于存储目标函数指针fn,返回后自动进入fn参数指定的函数。在arch_clone_func中,只用到了longjmp,恢复执行上下文,栈又恢复到了和父进程相同的状态,后续没有用到最初传入的arch_clone_stack

为什么要传入stack参数的中间部分?

  • CLONE_VM时,当父子进程共享同一个栈空间,为了避免冲突,将原有栈空间分一半使用,父子进程各占一半
  • 本场景没有启用CLONE_VM,所以笔者猜测是用于避免栈指针越过buf边界导致错误,所以取中间作为传入指针(欢迎有兴趣的读者在评论区讨论)

那么,可以不创建和使用栈吗?

理论上应当是可行的,glibc为了方便clone后的子进程进行下一步栈初始化等操作,提供了一个初始的函数fn指针。如果我们没有指定CLONE_VM,则可以用raw clone (syscall)。返回后的子进程与fork类似(实际上,fork就是clone系统调用的一个特殊情景),从调用clone的位置后继续运行。以下是manual中指明的两者的区别:

C library/kernel differences

The raw clone() system call corresponds more closely to fork(2) in that execution in the child continues from the point of the call. As such, the fn and arg arguments of the clone() wrapper function are omitted.

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: #if defined(ARCH_CYGWIN) || defined(ARCH_LINUX)是一个条件编译指令,用于判断是否定义了宏ARCH_CYGWIN或ARCH_LINUX。如果定义了其中一个宏,就会执行条件编译指令后面的代码块。\[1\]根据提供的引用内容,我们可以看到在引用\[1\]中没有直接提到ARCH_CYGWIN或ARCH_LINUX的定义,但可以推测这两个宏可能是用于指定操作系统平台的宏。在引用\[2\]中,可以看到ARCH_COORD_TYPE是一个浮点数精度的定义,而在引用\[3\]中,可以看到一些与C++特性相关的宏定义。因此,根据上下文推测,ARCH_CYGWIN和ARCH_LINUX可能是用于指定不同操作系统平台的宏定义。 #### 引用[.reference_title] - *1* [C/C++跨平台程序基础知识](https://blog.csdn.net/hnzwx888/article/details/84615947)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【QT】Qt Compiler Detection(编译)](https://blog.csdn.net/iEearth/article/details/76926977)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值