MatrixOne is a future-oriented hyper-converged cloud and edge native DBMS that supports transactional, analytical, and streaming workloads with a simplified and distributed database engine, across multiple data centers, clouds, edges and other heterogeneous infrastructures.[1]
A Btrfs subvolume is an independently mountable POSIX filetree and not a block device (and cannot be treated as one). Most other POSIX filesystems have a single mountable root, Btrfs has an independent mountable root for the volume (top level subvolume) and for each subvolume; a Btrfs volume can contain more than a single filetree, it can contain a forest of filetrees. A Btrfs subvolume can be thought of as a POSIX file namespace.[1]
/* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */
/** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ staticinlinevoidlist_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }
/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ staticinlinevoidlist_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); }
/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ staticinlinevoid __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; }
/** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ #ifndef CONFIG_DEBUG_LIST staticinlinevoidlist_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } #else externvoidlist_del(struct list_head *entry); #endif
关于这里,也没什么能产生太大疑惑的地方,唯一要好奇的可能是为什么要把被删除的链表节点的指针置为 LIST_POISON1 或 LIST_POISON2? 在用户态编程的时候,开发者们常把无效的指针置为 NULL 防止出现 Use After Free(UAF) 等问题的出现,一旦访问置为 NULL 的指针就能通过 Segment fault 得知发生了错误.但别忘了,Segment fault的检查是由内核完成的,在内核态编程时,自然是无法使用的.因此这里使用这两个特殊的地址触发分页保护告知开发者出现内存错误.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* * Architectures might want to move the poison pointer offset * into some well-recognized area such as 0xdead000000000000, * that is also not mappable by user-space exploits: */ #ifdef CONFIG_ILLEGAL_POINTER_VALUE # define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL) #else # define POISON_POINTER_DELTA 0 #endif
/* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) #define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
剩下的部分虽然也有很多内容,但都比较简单,相对来说也是易于理解的,Y7n05h 在这里就不赘述了.
这个宏函数还是很有趣的,能看到里面有很多 GNU 对 C 语言的扩展语法.直接从定义中看明白这个宏的用法是略有困难的,参考这个宏的用例将有助于理解.
1 2 3 4 5 6 7 8
/** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member)
1 2 3 4 5 6 7 8 9 10
/** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
而 head 是指向 container_of 到底做了什么呢?那就是根据结构体中的成员的地址,计算出结构体的地址.首先,抛开代码考虑这件事情,在给定结构体在确定体系结构上使用确定对齐方式,那么结构体成员相对结构体的偏移量就是一个编译期能确定的常量.那么若有了结构体成员的地址,那么减去相应的偏移量即可得到结构体的地址.这一切在理论上都是可行的,剩下的事只是如何用代码实现.
这里使用 typeof 进行类型推断,实现范型编程,声明获得与 member 相同的类型,并添加 * 获得 member 的指针类型.通过这一行,获得了指向结构体成员的指针.同时利用 (type *)( (char *)__mptr - offsetof(type,member) ) 根据 offsetof 关键字获得 member 在 type 中的偏移量,并使用指针运行将其从结构体成员的地址中减去. 最后则是使用 GNU 扩展的语句表达式语法,避免了需要将宏函数用 do{...}while(0) 包裹的麻烦事. 这些内容足够简单,但宏的运用与衔接十分精妙.
最后在谈谈
1 2 3 4 5 6 7 8
/** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; prefetch(pos->next), pos != (head); \ pos = pos->next)
_Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2, "PTRDIFF_MAX is not more than half of SIZE_MAX");
if (!__malloc_initialized) ptmalloc_init (); #if USE_TCACHE /* int_free also calls request2size, be careful to not pad twice. */ size_t tbytes; if (!checked_request2size (bytes, &tbytes)) { __set_errno (ENOMEM); returnNULL; } size_t tc_idx = csize2tidx (tbytes);
victim = _int_malloc (ar_ptr, bytes); /* Retry with another arena only if we were able to find a usable arena before. */ if (!victim && ar_ptr != NULL) { LIBC_PROBE (memory_malloc_retry, 1, bytes); ar_ptr = arena_get_retry (ar_ptr, bytes); victim = _int_malloc (ar_ptr, bytes); }
if (ar_ptr != NULL) __libc_lock_unlock (ar_ptr->mutex);
if (SINGLE_THREAD_P) av = &main_arena; else arena_get (av, sz);
if (av) { /* Check if we hand out the top chunk, in which case there may be no need to clear. */ #if MORECORE_CLEARS oldtop = top (av); oldtopsize = chunksize (top (av)); # if MORECORE_CLEARS < 2 /* Only newly allocated memory is guaranteed to be cleared. */ if (av == &main_arena && oldtopsize < mp_.sbrk_base + av->max_system_mem - (char *) oldtop) oldtopsize = (mp_.sbrk_base + av->max_system_mem - (char *) oldtop); # endif if (av != &main_arena) { heap_info *heap = heap_for_ptr (oldtop); if (oldtopsize < (char *) heap + heap->mprotect_size - (char *) oldtop) oldtopsize = (char *) heap + heap->mprotect_size - (char *) oldtop; } #endif } else { /* No usable arenas. */ oldtop = 0; oldtopsize = 0; } mem = _int_malloc (av, sz);
if (!SINGLE_THREAD_P) { if (mem == 0 && av != NULL) { LIBC_PROBE (memory_calloc_retry, 1, sz); av = arena_get_retry (av, sz); mem = _int_malloc (av, sz); }
if (av != NULL) __libc_lock_unlock (av->mutex); }
/* Allocation failed even after a retry. */ if (mem == 0) return0;
mchunkptr p = mem2chunk (mem);
/* If we are using memory tagging, then we need to set the tags regardless of MORECORE_CLEARS, so we zero the whole block while doing so. */ if (__glibc_unlikely (mtag_enabled)) return tag_new_zero_region (mem, memsize (p));
INTERNAL_SIZE_T csz = chunksize (p);
/* Two optional cases in which clearing not necessary */ if (chunk_is_mmapped (p)) { if (__builtin_expect (perturb_byte, 0)) returnmemset (mem, 0, sz);
return mem; }
#if MORECORE_CLEARS if (perturb_byte == 0 && (p == oldtop && csz > oldtopsize)) { /* clear only the bytes from non-freshly-sbrked memory */ csz = oldtopsize; } #endif
/* Unroll clear of <= 36 bytes (72 if 8byte sizes). We know that contents have an odd number of INTERNAL_SIZE_T-sized words; minimally 3. */ d = (INTERNAL_SIZE_T *) mem; clearsize = csz - SIZE_SZ; nclears = clearsize / sizeof (INTERNAL_SIZE_T); assert (nclears >= 3);
/* While we're here, if we see other chunks of the same size, stash them in the tcache. */ size_t tc_idx = csize2tidx (nb); if (tcache && tc_idx < mp_.tcache_bins) { mchunkptr tc_victim;
/* While bin not empty and tcache not full, copy chunks. */ while (tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = *fb) != NULL) { if (__glibc_unlikely (misaligned_chunk (tc_victim))) malloc_printerr ("malloc(): unaligned fastbin chunk detected 3"); if (SINGLE_THREAD_P) *fb = REVEAL_PTR (tc_victim->fd); else { REMOVE_FB (fb, pp, tc_victim); if (__glibc_unlikely (tc_victim == NULL)) break; } tcache_put (tc_victim, tc_idx); } }
v7 = __readfsqword(0x28u); sub_40091D(a1, a2, a3); puts("Happy to see you darling!"); puts("Give me your name:"); read(0, buf, 0x10uLL); puts("Give me your key:"); read(0, v4, 0x20uLL); puts("Now start the game!"); do { puts("Input your password!:"); read(0, v6, 0x2CuLL); result = sub_400DB8(v6); } while ( (_DWORD)result != 1 ); return result; }
Y7n05h 在伪码中没有找到任何漏洞.
难道这是个栈题,漏洞存在于 sub_400BFD() 里面吗?
通过 IDA 插件 Findcrypt 得到 TEA_DELTA_400C64 的内容.通过搜索引擎得知 TEA 加密算法.
#include<stdio.h> #include<unistd.h> intmain(void) { setvbuf(stderr, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); char buf[0x50]; puts("Happy to see you darling!"); puts("Give me your name:"); read(0, buf, 0x10uLL); puts("Give me your key:"); read(0, buf, 0x20uLL); puts("Now start the game!"); do { puts("Input your password!:"); read(0, buf, 0x2CuLL); } while (1); }