这里探索了很多方式,勘误之前的启动方式实际上存在一些问题,这里终于探索到了最完备的musl 调试环境,这里特别感谢我的学弟gxh为勘误做出的贡献
提示:
musl 的库函数就只有一个libc.so ,这个libc.so 既充当ld,又充当libc
最佳环境配置:
本机环境ubuntu:20.04
或:通过包管理器安装,
关于自己编译源码:
自己编译源码具体可以参考下面这篇文档
blog.fpliu.com/it/software/musl-libc
musl-dbgsym_1.2.2-1_amd64.ddeb
这里安装调试符号非常重要,关系到后面调试!!!
在安装调试符号后,即使musl 程序的libc.so不带调试信息,gdb 还是会从我们安装的调试符号中自动寻找匹配,还是能做到带符号调试!!!
安利xf1les 师傅编写的musl heap gdb 插件
Requirements:(环境需求)
Python 3.5.2+
GDB 7.11.1+ with python3 support
musl libc 1.2.1+
with debug symbols
安装后非常方便,可以很方便的看__malloc_context 结构体,和meta 链表的情况,还可以直接得到一些musl利用的必要函数的偏移
替换默认环境下的libc.so
把下载的musl的默认libc.so 换为指定的libc.so, 默认路径:/usr/lib/x86_64-linux-musl/libc.so
,这里可以先备份再替换
p = process(rbin)
直接启动程序,他会自动使用默认的libc
地址空间布局效果图
这个才是正确的布局,之前因为第五空间的题远程靶机启动方式错误被误导了,导致从开始学习的时候,就是按照错误方式启动的
(之前的文章介绍的错误方式,对不住各位师傅们)
或者:patchelf
把libc.so 当作ld 来patch (这种方式也可以)
此方式经过测试也是与直接替换默认的libc.so 的结果是相同的
因为安装了musl libc debug symbols 所以我们可以直接p __malloc_context
查看meta 堆管理器结构体
可以利用xf1les 师傅的gdb 插件,直接查看当前的chunk 被nontrivial_free()释放的结果,对于利用dequeue 进行地址互写非常方便直观看见结果
致歉:再次强调
chunk->group->meta 分析顺序
先从musl 的基本数据结构 这里我选择从小到大来理解,因为在源码里它就是从小到大索引的
从chunk 一路索引到 meta。
首先介绍chunk
每个chunk 都有个4 字节的chunk头记录 idx 和 offset
(第一个chunk 比较特殊,因为它上面是 group结构 + chunk 头=0x10 )
很多没有遇见过的问题:且不是实验内容类的,比如虚拟机网卡,服务器环境,实验难度
在释放后 chunk 头的 idx会变成0xff offset 会清零
这里 offset 和 idx 比较重要
细节:和glibc 的chunk 类似 glibc chunk 可以占用下一个chunk 的prev_size 空间
而musl 可以使用 下一个chunk 头的低4B 来储存数据
从chunk 索引到 group:
源码:
根据源码我们可以知道 从chunk 索引到group 起始地址的计算式子为
group_addr = chunk_addr - 0x10 * offset - 0x10
补充
offset = p[-2] (这里的p 就是代指chunk)
index 从 get_slot_index(p)中得到
其中如果这个meta 前后都没有,那么它的prev next 就指向它自己
avail_mask,free_mask 是bitmap 的形式体现 chunk 的状态
这里例子是我申请了3个 0x30的chunk1、2、3, 然后free 掉chunk2
avail_mask == b"01111000" (最前面那个0 不算只是为了对齐)
在 avail_mask 中 2 进制的 0 表示不可分配 1表示可分配,顺序是从后到前
如01111000 中最后的 3个0 , 表示第1、2、3个 chunk 是不可分配的 前面4个chunk 是可以分配的
free_mask == 2 =0010
在 free_mask 中的 1 表示已经被释放
如第二个chunk2已经被释放:free_mask =b"0010"
last_idx 可以表示最多可用堆块的数量 最多数量=last_idx+1(因为是从0 - last_idx)
freeable=1根据源码 代表meta否可以被回收 freeable=0 代表不可以 =1 代表可以
sizeclass=3 表示由0x3
这个group进行管理这一类的大小的chunk
maplen
maplen >= 1表示这个meta里的group 是新mmap出来的,长度为多少
meta->maplen = (needed+4095)/4096;
并且这个group 不在size_classes里
maplen =0 表示group 不是新mmap 出来的在size_classes里
细节:
meta 一般申请的是堆空间brk 分配的,有可能是mmap 映射的,而group 都是使用的mmap 的空间
由于bitmap的限制, 因此一个group中最多只能有32个chunk
meta_area 是管理meta的合集 meta_area 以页为单位分配 所以计算地址如下
meta_area_addr = meta & ( -4096 )
const struct meta_area area = (void )((uintptr_t)meta & -4096)
check:是个校验数字 保护meta_area 里的meta,防止meta被 伪造
meta_area *next 指向下一个meta_area 如果没有 就默认为0
nslots: meta 槽的数量
细节:在这个meta_area 页被使用的时候 上一个临近的页 会被设置为不可写
是为了防止 使用者覆盖check 校验值
是musl libc 记录结构状态的表,记录各个meta 和 secret 队列信息等
小总结一下
(如果不想看源码 可以跳下面看总结)
源码路径
/src/malloc/mallocng/malloc.c
源码:
!!!! 关键: 一般分配先进入这个循环
一、判断是否超过size 阈值
二、分配chunk
如果active 对应size的meta 位置上为空,没找到那么尝试先找size更大的meta
如果active 对应size的meta位置上有对应的meta,尝试从这个meta中的group找到可用的chunk(这里malloc 那个循环:for (;;)
,
这里不清楚建议看malloc源码分析那里)
仔细观察分配的过程,我们也可以看出为什么free 的chunk不能立即回收使用,因为有空闲的chunk的时候,分配chunk是直接设置meta->avail_mask
然后直接enframe() 直接从group中取出 chunk即可,不会设置meta->freed
源码路径
/src/malloc/mallocng/malloc.c
free:
细节:!!!
get_meta
关于nontrivial_free()函数很重要 ,这里尽量详细说明
dequeue 触发条件
self = 1 << idx
下面是几种简单的触发情况
1.avail_mask 表示只有一个chunk 被使用 ,freed_mask=0,而free 刚好要free 一个chunk
满足 okay_to_free() 条件 就可以进入dequeue 进行出队操作
如add(1,0x20) 再free(1) 就会使得meta 被回收
2.avail_mask=0, freed_mask 表示只有 1个 chunk 没被 释放,这时释放的chunk 就应该是那最后一个chunk
如下面情况 avail_mask ==0 free_mask=63=00111111 last_idx = 6
已经释放6 个chunk 还有最后一个chunk没被释放 在释放最后一个chunk 时会触发dequeue使得对应meta出队
3.如果发现这个group中所有的chunk要么被free, 要么是可用的, 那么就会回收掉这个group,调用dequeue从队列中出队
一般有如下几种利用方法,核心原理都是构造假的chunk 索引到假的group 从而所引导假的meta
或覆盖group 中指向meta 的指针 覆盖为假的meta ,然后使得假的meta dequeue 最终实现unlink
(构造fake_meta 需要先泄露 secret 校验值)
一、
通过构造假的meta 满足各种条件 通过以下流程
free()->nontrivial_free()->dequeue
这里通过free 到 dequeue
二、
通过realloc 里也带有free
realloc()->free(old)->nontrivial_free()->dequeue
注意: musl 是没有malloc_hook和 free_hook 这种一般的hook 位
且musl 程序的IO_FILE 结构体格式和libc 不一样 没有IO_jump_t的vtable
但是存在read,write,seek,close 四个函数指针
讲一下思路
通过任意地址互写指针,向stdout_used 写入我们伪造的fake_stdout地址, 通过IO_FILE 劫持程序执行流
到我们布置好的fake_stdout 上,可以找IO_FILE 里的一些函数exit puts在fake_stdout上布置rop_chain然后通过栈迁移的gadget 利用FSOP 劫持程序到布置的fake_stdout上
在利用指针互写 修改fake_meta 中的mem(mem 就是group 区域) ,把mem 修改为我们想要的地址,
然后让fake_meta 通过queue 入队,可以实现任意地址分配的,然后同样是打 IO_FILE 通过修改stdout stdin 和stderr 结构体 劫持程序流
前面提到过
处于这两种状态的group 对应的meta 会被dequeue 出队
一些特殊的地方:
若:group 中chunk都被使用了,只有一个被free的chunk, 在再次malloc 申请相同size chunk时:
meta 结构体有prev,next 指针。同一size的chunk 对应同一类meta。再queue 后,同一类meta被链接,实际上是个链表结构
若:当前meta 对应group 中无可用chunk,则在alloc_slot 中,会在链表上的寻找下一个meta的group中是否有可用的chunk
借助DefCon Quals 2021的mooosl学习musl mallocng(源码审计篇)
musl-1.2.x堆部分源码分析
sudo dpkg
-
i musl_1.
2.2
-
1_amd64
.deb
sudo dpkg
-
i musl_1.
2.2
-
1_amd64
.deb
sudo apt
-
get install
-
y musl musl
-
dev
sudo apt
-
get install
-
y musl musl
-
dev
sudo dpkg
-
i musl
-
dbgsym_1.
2.2
-
1_amd64
.ddeb
sudo dpkg
-
i musl
-
dbgsym_1.
2.2
-
1_amd64
.ddeb
git clone https:
/
/
github.com
/
xf1les
/
muslheap.git
git clone https:
/
/
github.com
/
xf1les
/
muslheap.git
echo
"source /path/to/muslheap.py"
>> ~
/
.gdbinit
echo
"source /path/to/muslheap.py"
>> ~
/
.gdbinit
patchelf
-
-
set
-
interpreter .
/
libc.so .
/
rbin
patchelf
-
-
set
-
interpreter .
/
libc.so .
/
rbin
dir
/
path
/
to
/
musl
-
1.2
.
2
/
src
/
malloc
/
和
dir
/
path
/
to
/
musl
-
1.2
.
2
/
src
/
malloc
/
mallocng便可
dir
/
path
/
to
/
musl
-
1.2
.
2
/
src
/
malloc
/
和
dir
/
path
/
to
/
musl
-
1.2
.
2
/
src
/
malloc
/
mallocng便可
io
=
process([libc.so,rbin]) 这种启动方式是错误的!!!!
io
=
process([libc.so,rbin]) 这种启动方式是错误的!!!!
struct chunk{
char prev_user_data[];
uint8_t idx;
/
/
低
5bit
为idx第几个chunk
uint16_t offset;
/
/
与第一个chunk起始地址的偏移,实际地址偏移为offset
*
UNIT,详细请看get_meta源码中得到group地址的而过程!
char data[];
};
struct chunk{
char prev_user_data[];
uint8_t idx;
/
/
低
5bit
为idx第几个chunk
uint16_t offset;
/
/
与第一个chunk起始地址的偏移,实际地址偏移为offset
*
UNIT,详细请看get_meta源码中得到group地址的而过程!
char data[];
};
struct group {
struct meta
*
meta;
unsigned char active_idx:
5
;
char pad[UNIT
-
sizeof(struct meta
*
)
-
1
];
/
/
padding
=
0x10B
unsigned char storage[];
/
/
chunks
};
struct group {
struct meta
*
meta;
unsigned char active_idx:
5
;
char pad[UNIT
-
sizeof(struct meta
*
)
-
1
];
/
/
padding
=
0x10B
unsigned char storage[];
/
/
chunks
};
static inline struct meta
*
get_meta(const unsigned char
*
p)
{
assert
(!((uintptr_t)p &
15
));
int
offset
=
*
(const uint16_t
*
)(p
-
2
);
int
index
=
get_slot_index(p);
if
(p[
-
4
]) {
assert
(!offset);
offset
=
*
(uint32_t
*
)(p
-
8
);
assert
(offset >
0xffff
);
}
const struct group
*
base
=
(const void
*
)(p
-
UNIT
*
offset
-
UNIT);
/
/
base 指向的就是group 地址
............
}
static inline struct meta
*
get_meta(const unsigned char
*
p)
{
assert
(!((uintptr_t)p &
15
));
int
offset
=
*
(const uint16_t
*
)(p
-
2
);
int
index
=
get_slot_index(p);
if
(p[
-
4
]) {
assert
(!offset);
offset
=
*
(uint32_t
*
)(p
-
8
);
assert
(offset >
0xffff
);
}
const struct group
*
base
=
(const void
*
)(p
-
UNIT
*
offset
-
UNIT);
/
/
base 指向的就是group 地址
............
}
static inline
int
get_slot_index(const unsigned char
*
p)
{
return
p[
-
3
] &
31
;
}
static inline
int
get_slot_index(const unsigned char
*
p)
{
return
p[
-
3
] &
31
;
}
struct meta {
struct meta
*
prev,
*
next
;
/
/
双向链表
struct group
*
mem;
/
/
这里指向管理的group 地址
volatile
int
avail_mask, freed_mask;
uintptr_t last_idx:
5
;
uintptr_t freeable:
1
;
uintptr_t sizeclass:
6
;
uintptr_t maplen:
8
*
sizeof(uintptr_t)
-
12
;
};
struct meta {
struct meta
*
prev,
*
next
;
/
/
双向链表
struct group
*
mem;
/
/
这里指向管理的group 地址
volatile
int
avail_mask, freed_mask;
uintptr_t last_idx:
5
;
uintptr_t freeable:
1
;
uintptr_t sizeclass:
6
;
uintptr_t maplen:
8
*
sizeof(uintptr_t)
-
12
;
};
static
int
okay_to_free(struct meta
*
g)
{
int
sc
=
g
-
>sizeclass;
if
(!g
-
>freeable)
return
0
;
...........
}
static
int
okay_to_free(struct meta
*
g)
{
int
sc
=
g
-
>sizeclass;
if
(!g
-
>freeable)
return
0
;
...........
}
const uint16_t size_classes[]
=
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
12
,
15
,
18
,
20
,
25
,
31
,
36
,
42
,
50
,
63
,
72
,
84
,
102
,
127
,
146
,
170
,
204
,
255
,
292
,
340
,
409
,
511
,
584
,
682
,
818
,
1023
,
1169
,
1364
,
1637
,
2047
,
2340
,
2730
,
3276
,
4095
,
4680
,
5460
,
6552
,
8191
,
};
const uint16_t size_classes[]
=
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
12
,
15
,
18
,
20
,
25
,
31
,
36
,
42
,
50
,
63
,
72
,
84
,
102
,
127
,
146
,
170
,
204
,
255
,
292
,
340
,
409
,
511
,
584
,
682
,
818
,
1023
,
1169
,
1364
,
1637
,
2047
,
2340
,
2730
,
3276
,
4095
,
4680
,
5460
,
6552
,
8191
,
};
struct meta_area {
uint64_t check;
struct meta_area
*
next
;
int
nslots;
struct meta slots[];
};
struct meta_area {
uint64_t check;
struct meta_area
*
next
;
int
nslots;
struct meta slots[];
};
struct malloc_context {
uint64_t secret;
/
/
和meta_area 头的check 是同一个值 就是校验值
size_t pagesize;
int
init_done;
/
/
是否初始化标记
unsigned mmap_counter;
/
/
记录有多少mmap 的内存的数量
struct meta
*
free_meta_head;
/
/
被free 的meta 头 这里meta 管理使用了队列和双向循环链表
struct meta
*
avail_meta;
/
/
指向可用meta数组
size_t avail_meta_count, avail_meta_area_count, meta_alloc_shift;
struct meta_area
*
meta_area_head,
*
meta_area_tail;
unsigned char
*
avail_meta_areas;
struct meta
*
active[
48
];
/
/
记录着可用的meta
size_t u sage_by_class[
48
];
uint8_t unmap_seq[
32
], bounces[
32
];
uint8_t seq;
uintptr_t brk;
};
struct malloc_context {
uint64_t secret;
/
/
和meta_area 头的check 是同一个值 就是校验值
size_t pagesize;
int
init_done;
/
/
是否初始化标记
unsigned mmap_counter;
/
/
记录有多少mmap 的内存的数量
struct meta
*
free_meta_head;
/
/
被free 的meta 头 这里meta 管理使用了队列和双向循环链表
struct meta
*
avail_meta;
/
/
指向可用meta数组
size_t avail_meta_count, avail_meta_area_count, meta_alloc_shift;
struct meta_area
*
meta_area_head,
*
meta_area_tail;
unsigned char
*
avail_meta_areas;
struct meta
*
active[
48
];
/
/
记录着可用的meta
size_t u sage_by_class[
48
];
uint8_t unmap_seq[
32
], bounces[
32
];
uint8_t seq;
uintptr_t brk;
};
void
*
malloc(size_t n)
{
if
(size_overflows(n))
return
0
;
/
/
最大申请空间限制
struct meta
*
g;
uint32_t mask, first;
int
sc;
int
idx;
int
ctr;
if
(n >
=
MMAP_THRESHOLD) {
/
/
size >
=
阈值 会直接通过mmap 申请空间
size_t needed
=
n
+
IB
+
UNIT;
/
/
UNIT
0x10
IB
4
定义在meta.h 里 这里UNIT
+
IB 是一个基本头的大小
void
*
p
=
mmap(
0
, needed, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,
-
1
,
0
);
/
/
新mmap group 空间
if
(p
=
=
MAP_FAILED)
return
0
;
wrlock();
step_seq();
g
=
alloc_meta();
if
(!g) {
/
/
如果申请meta 失败 会把刚刚mmap 出来的group 回收
unlock();
munmap(p, needed);
/
/
回收group
return
0
;
}
g
-
>mem
=
p;
/
/
mem
=
group 地址
g
-
>mem
-
>meta
=
g;
/
/
group 头部 指向meta (g 为 meta)
g
-
>last_idx
=
0
;
/
/
mmap的group last_idx默认值
=
0
g
-
>freeable
=
1
;
g
-
>sizeclass
=
63
;
/
/
mmap 的申请的 sizeclass 都为
63
g
-
>maplen
=
(needed
+
4095
)
/
4096
;
g
-
>avail_mask
=
g
-
>freed_mask
=
0
;
ctx.mmap_counter
+
+
;
/
/
mmap 内存记载数量
+
+
idx
=
0
;
goto success;
}
/
/
否则直接根据传入size,转换成size_classes的对应大小的 下标,
sc
=
size_to_class(n);
rdlock();
g
=
ctx.active[sc];
/
/
从现有的active中取出对应sc 的 meta ,不同sc 对应不同的meta
/
*
如果从ctx.active 中没找到对应的meta 会执行下面的
if
分支
这里!g<
=
> g
=
=
0
,说明ctx.active[sc] 没有对应的meta
*
/
if
(!g && sc>
=
4
&& sc<
32
&& sc!
=
6
&& !(sc&
1
) && !ctx.usage_by_class[sc]) {
size_t usage
=
ctx.usage_by_class[sc|
1
];
/
/
如果在 ctx.active 没找到 就使用更大size group 的meta
/
/
if
a new group may be allocated, count it toward
/
/
usage
in
deciding
if
we can use coarse
class
.
if
(!ctx.active[sc|
1
] || (!ctx.active[sc|
1
]
-
>avail_mask
&& !ctx.active[sc|
1
]
-
>freed_mask))
usage
+
=
3
;
if
(usage <
=
12
)
sc |
=
1
;
g
=
ctx.active[sc];
}
for
(;;) {
mask
=
g ? g
-
>avail_mask :
0
;
first
=
mask&
-
mask;
if
(!first)
break
;
if
(RDLOCK_IS_EXCLUSIVE || !MT)
g
-
>avail_mask
=
mask
-
first;
else
if
(a_cas(&g
-
>avail_mask, mask, mask
-
first)!
=
mask)
continue
;
idx
=
a_ctz_32(first);
goto success;
}
upgradelock();
idx
=
alloc_slot(sc, n);
/
*
如果当前group 不满足就会来到这里:
alloc_slot 从group 中取出对应大小chunk 的idx
这里先从对应sc 的ctx.active[sc] 中找对应的meta的group 有无空闲chunk可以使用
再从队列中其他meta的group 中找
如果队列中其他meta的group 有可利用的chunk,就使用
如果没有就重新分配一个新的group
*
/
if
(idx <
0
) {
unlock();
return
0
;
}
g
=
ctx.active[sc];
/
/
取出 sc 对应active meta
success:
ctr
=
ctx.mmap_counter;
unlock();
return
enframe(g, idx, n, ctr);
/
/
从对应meta 中的group 取出 第idx号chunk n
=
size
}
void
*
malloc(size_t n)
{
if
(size_overflows(n))
return
0
;
/
/
最大申请空间限制
struct meta
*
g;
uint32_t mask, first;
int
sc;
int
idx;
int
ctr;
if
(n >
=
MMAP_THRESHOLD) {
/
/
size >
=
阈值 会直接通过mmap 申请空间
size_t needed
=
n
+
IB
+
UNIT;
/
/
UNIT
0x10
IB
4
定义在meta.h 里 这里UNIT
+
IB 是一个基本头的大小
void
*
p
=
mmap(
0
, needed, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,
-
1
,
0
);
/
/
新mmap group 空间
if
(p
=
=
MAP_FAILED)
return
0
;
wrlock();
step_seq();
g
=
alloc_meta();
if
(!g) {
/
/
如果申请meta 失败 会把刚刚mmap 出来的group 回收
unlock();
munmap(p, needed);
/
/
回收group
return
0
;
}
g
-
>mem
=
p;
/
/
mem
=
group 地址
g
-
>mem
-
>meta
=
g;
/
/
group 头部 指向meta (g 为 meta)
g
-
>last_idx
=
0
;
/
/
mmap的group last_idx默认值
=
0
g
-
>freeable
=
1
;
g
-
>sizeclass
=
63
;
/
/
mmap 的申请的 sizeclass 都为
63
g
-
>maplen
=
(needed
+
4095
)
/
4096
;
g
-
>avail_mask
=
g
-
>freed_mask
=
0
;
ctx.mmap_counter
+
+
;
/
/
mmap 内存记载数量
+
+
idx
=
0
;
goto success;
}
/
/
否则直接根据传入size,转换成size_classes的对应大小的 下标,
sc
=
size_to_class(n);
rdlock();
g
=
ctx.active[sc];
/
/
从现有的active中取出对应sc 的 meta ,不同sc 对应不同的meta
/
*
如果从ctx.active 中没找到对应的meta 会执行下面的
if
分支
这里!g<
=
> g
=
=
0
,说明ctx.active[sc] 没有对应的meta
*
/
if
(!g && sc>
=
4
&& sc<
32
&& sc!
=
6
&& !(sc&
1
) && !ctx.usage_by_class[sc]) {
size_t usage
=
ctx.usage_by_class[sc|
1
];
/
/
如果在 ctx.active 没找到 就使用更大size group 的meta
/
/
if
a new group may be allocated, count it toward
/
/
usage
in
deciding
if
we can use coarse
class
.
if
(!ctx.active[sc|
1
] || (!ctx.active[sc|
1
]
-
>avail_mask
&& !ctx.active[sc|
1
]
-
>freed_mask))
usage
+
=
3
;
if
(usage <
=
12
)
sc |
=
1
;
g
=
ctx.active[sc];
}
for
(;;) {
mask
=
g ? g
-
>avail_mask :
0
;
first
=
mask&
-
mask;
if
(!first)
break
;
if
(RDLOCK_IS_EXCLUSIVE || !MT)
g
-
>avail_mask
=
mask
-
first;
else
if
(a_cas(&g
-
>avail_mask, mask, mask
-
first)!
=
mask)
continue
;
idx
=
a_ctz_32(first);
goto success;
}
upgradelock();
idx
=
alloc_slot(sc, n);
/
*
如果当前group 不满足就会来到这里:
alloc_slot 从group 中取出对应大小chunk 的idx
这里先从对应sc 的ctx.active[sc] 中找对应的meta的group 有无空闲chunk可以使用
再从队列中其他meta的group 中找
如果队列中其他meta的group 有可利用的chunk,就使用
如果没有就重新分配一个新的group
*
/
if
(idx <
0
) {
unlock();
return
0
;
}
g
=
ctx.active[sc];
/
/
取出 sc 对应active meta
success:
ctr
=
ctx.mmap_counter;
unlock();
return
enframe(g, idx, n, ctr);
/
/
从对应meta 中的group 取出 第idx号chunk n
=
size
}
for
(;;) {
mask
=
g ? g
-
>avail_mask :
0
;
/
/
先检查g所指meta是否存在,若存在mask
=
g
-
>avail_mask
first
=
mask&
-
mask;
/
/
这里只有mask
=
0
时,first才会为
0
if
(!first)
break
;
/
/
mask为
0
,first
=
0
,无可用空闲chunk,跳出循环
if
(RDLOCK_IS_EXCLUSIVE || !MT)
/
/
如果是排它锁, 那么下面保证成功
g
-
>avail_mask
=
mask
-
first;
else
if
(a_cas(&g
-
>avail_mask, mask, mask
-
first)!
=
mask)
/
/
成功找到并设置avail_mask之后,
continue
后设置idx,然后跳出
continue
;
idx
=
a_ctz_32(first);
goto success;
}
upgradelock();
如果
idx
=
alloc_slot(sc, n);
for
(;;) {
mask
=
g ? g
-
>avail_mask :
0
;
/
/
先检查g所指meta是否存在,若存在mask
=
g
-
>avail_mask
first
=
mask&
-
mask;
/
/
这里只有mask
=
0
时,first才会为
0
if
(!first)
break
;
/
/
mask为
0
,first
=
0
,无可用空闲chunk,跳出循环
if
(RDLOCK_IS_EXCLUSIVE || !MT)
/
/
如果是排它锁, 那么下面保证成功
g
-
>avail_mask
=
mask
-
first;
else
if
(a_cas(&g
-
>avail_mask, mask, mask
-
first)!
=
mask)
/
/
成功找到并设置avail_mask之后,
continue
后设置idx,然后跳出
continue
;
idx
=
a_ctz_32(first);
goto success;
}
upgradelock();
如果
idx
=
alloc_slot(sc, n);
static
int
alloc_slot(
int
sc, size_t req)
{
/
/
尝试从限制active 中找到合适可用的
uint32_t first
=
try_avail(&ctx.active[sc]);
if
(first)
return
a_ctz_32(first);
/
/
如果没找到 重新创造一个meta,然后重新分配一个size大小对应sc的group,给这个新分配的meta
struct meta
*
g
=
alloc_group(sc, req);
if
(!g)
return
-
1
;
g
-
>avail_mask
-
-
;
queue(&ctx.active[sc], g);
/
/
把新meta 加入队列
return
0
;
}
static
int
alloc_slot(
int
sc, size_t req)
{
/
/
尝试从限制active 中找到合适可用的
uint32_t first
=
try_avail(&ctx.active[sc]);
if
(first)
return
a_ctz_32(first);
/
/
如果没找到 重新创造一个meta,然后重新分配一个size大小对应sc的group,给这个新分配的meta
struct meta
*
g
=
alloc_group(sc, req);
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2022-5-14 21:46
被0xRGz编辑
,原因: 更新错误