1) IJON-ENABLE:为了实现IJON-ENABLE,我们引入了一个应用于所有位图索引计算的掩码。如果掩码设置为零,则只能寻址和更新第一个位图条目。如果设置为0xFFFF,则使用原始行为。这样,我们可以有效地随意禁用和启用覆盖率跟踪。
afl-llvm-rt.o.c
void ijon_enable_feedback(){
__afl_mask = 0xffffffff;
}
void ijon_disable_feedback(){
__afl_mask = 0x0;
}
2) IJON-INC和IJON-SET:这两种注释都支持与位图的直接交互,因此实现非常简单。调用IJON-INC(n)时,位图中的第n个条目将递增。类似地,调用IJON-SET(n)将第n个条目的最低有效位设置为1。
如果这些函数在程序代码中的多个位置使用,必须非常小心,不要重复使用相同的位图索引。为了避免这种情况,我们引入了辅助宏IJON_INC(m)和IJON_SET(m)。两个宏都调用相应的函数,但根据m的散列以及宏调用的文件名和行号计算位图索引n。因此,避免了对常用参数(如0或1)的琐碎冲突。
afl-rt.h
#define IJON_BITS(x) ((x==0)?{0}:__builtin_clz(x))
#define IJON_INC(x) ijon_map_inc(ijon_hashstr(__LINE__,__FILE__)^(x))
#define IJON_SET(x) ijon_map_set(ijon_hashstr(__LINE__,__FILE__)^(x))
afl-llvm-rt.o.c
uint64_t ijon_simple_hash(uint64_t x) {
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
uint32_t ijon_hashint(uint32_t old, uint32_t val){
uint64_t input = (((uint64_t)(old))<<32) | ((uint64_t)(val));
return (uint32_t)(ijon_simple_hash(input));
}
uint32_t ijon_hashmem(uint32_t old, char* val, size_t len){
old = ijon_hashint(old,len);
for(size_t i = 0; i < len ; i++){
old = ijon_hashint(old, val[i]);
}
return old;
}
uint32_t ijon_hashstr(uint32_t old, char* val){
return ijon_hashmem(old, val, strlen(val));
}
void ijon_map_inc(uint32_t addr){
__afl_area_ptr[(__afl_state^addr)%MAP_SIZE]+=1;
}
void ijon_map_set(uint32_t addr){
__afl_area_ptr[(__afl_state^addr)%MAP_SIZE]|=1;
}
3) IJON-STATE:当调用IJON_STATE(n)时,我们更改基本块边映射到位图条目的方式。为此,我们修改了边缘元组的定义,除了源ID和目标ID之外,还包括状态:(state,ids,idt)。这里,“state”是一个线程局部变量,它存储与当前状态相关的信息。调用IJON-STATE(n)更新state := state⊕n.这样,两个连续的呼叫就会相互抵消。
我们还修改了编译器过程,使得位图索引的计算如下state⊕(ids∗2)⊕idt。这样,每次状态变量更改时,每个边都会在位图中获得一个新索引。这些技术允许相同的代码在不同的上下文中执行时产生不同的覆盖率。
#define IJON_CTX(x) ({ uint32_t hash = hashstr(__LINE__,__FILE__); ijon_xor_state(hash); __typeof__(x) IJON_UNIQ_NAME() = (x); ijon_xor_state(hash); IJON_UNIQ_NAME(); })
4) IJON-MAX:为了最大化,我们扩展了fuzzer来维护额外的第二个输入队列。我们支持最大化多达512个不同的变量。这些变量中的每一个都称为插槽。fuzzer只存储为每个插槽生成最佳值的输入,并丢弃导致较小值的旧输入。为了存储观察到的最大值,我们引入了一个额外的共享内存最大映射,该映射由64位无符号整数组成。调用最大化原语IJON_MAX(slot,val)更新maxmap[slot]=max(maxmap[slot],val)。
#define IJON_MAX(x) ijon_max(ijon_hashstr(__LINE__,__FILE__),(x))
#define IJON_MIN(x) ijon_max(ijon_hashstr(__LINE__,__FILE__),0xffffffffffffffff-(x))
5)辅助函数:IJON的运行库中包含了一组辅助函数,可以简化常见的注释。例如,运行时库包含帮助函数,用于散列不同类型的数据,如字符串、内存缓冲区、活动函数的堆栈或当前行号和文件名。此外,还有一些辅助函数可以用来计算两个值之间的差值。我们实现了不同的辅助函数来简化注释过程,如下所述:
•IJON_CMP(x,y):计算x和y之间不同的比特数。这个辅助函数直接使用它来触摸位图中的一个字节。值得一提的是,出于实际目的,它并没有直接使用不同位的数量作为位图的索引。考虑清单5,如果在多个位置重用相同的注释,那么索引将发生冲突(两者共享范围0到64)。相反,IJON_CMP将参数与当前文件名和行的散列组合在一起。因此,我们大大减少了碰撞的机会。
#define IJON_CMP(x,y) IJON_INC(__builtin_popcount((x)^(y)))
•IJON_HASH_INT(u32 old, u32 val):返回两个类型的哈希值。如III-D节所述,我们使用哈希函数来创建更多样化的索引集,并减少碰撞的概率。
•IJON_HASH_STR(u32 old,char* str):返回两个参数的哈希值。例如,我们使用这个帮助函数来创建文件名的散列。
•IJON_HASH_MEM(u32 old, u8* mem, size_t len):返回一个旧的散列和第一个len字节的mem。
•IJON_HASH_STACK (u32 old):返回活动函数返回地址的哈希值(我们称之为执行上下文)。所有地址都被单独散列。结果值通过xor组合在一起产生最终结果。这样,递归调用就不会创建太多不同的值。这个辅助函数可以用于根据散列执行上下文创建反馈的虚拟副本。
•IJON_STRDIST(char* a, char* b):计算并返回a和b的公共前缀的长度。
#define IJON_DIST(x,y) ijon_min(ijon_hashstr(__LINE__,__FILE__), _IJON_ABS_DIST(x,y))
#define IJON_STRDIST(x,y) IJON_SET(ijon_hashint(ijon_hashstack(), ijon_strdist(x,y)))
还有
#define _IJON_CONCAT(x, y) x##y
#define _IJON_UNIQ_NAME() IJON_CONCAT(temp,__LINE__)
#define _IJON_ABS_DIST(x,y) ((x)<(y) ? (y)-(x) : (x)-(y))
#define IJON_BITS(x) ((x==0)?{0}:__builtin_clz(x))
https://blog.csdn.net/czg13548930186/article/details/72860876
##被称为连接符,用来将两个宏参数连接为一个宏参数。
— Built-in Function: int __builtin_clz (unsigned int x)
返回前面引导位0 的个数,如果x=0 的话,结果未知;
__builtin_clz(unsigned x); // count leading zeros
__builtin_ctz(unsigned x); // count trailing zeros
__builtin_popcount(unsigned x); // 1的个数
// ffs, parity 感觉没啥用。前者返回ctz+1,后者返回popcount&1
// 另:与大多数函数一样,后缀 l 表示long型参数,后缀 ll 表示long long型参数。
__builtin_popcountll(unsigned long long x);