Linux社区关于链表的bug讨论我们要看一下
最近在Linux社区看到一个关于内核链表的讨论
原文讨论链接:
https://lwn.net/SubscriberLink/885941/01fdc39df2ecc25f/
先用例子说明怎么使用内核链表
list.h
/* SPDX-License-Identifier: GPL-2.0 */#ifndef LIST_H#defineLIST_H/** Copied from include/linux/...*/#undefoffsetof#defineoffsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)/*** 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.**/#definecontainer_of(ptr, type, member) ({ \consttypeof(((type *)0)->member ) *__mptr = (ptr); \(type *)( (char*)__mptr - offsetof(type,member) );})structlist_head {structlist_head *next, *prev;};#defineLIST_HEAD_INIT(name) { &(name), &(name) }#defineLIST_HEAD(name) \structlist_head name = LIST_HEAD_INIT(name)/*** 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_head within the struct.*/#definelist_entry(ptr, type, member) \container_of(ptr, type, member)/*** list_for_each_entry - iterate over list of given type* @pos: the type * to use as a loop cursor.* @head: the head for your list.* @member: the name of the list_head within the struct.*/#definelist_for_each_entry(pos, head, member) \for(pos = list_entry((head)->next, typeof(*pos), member); \&pos->member != (head); \pos = list_entry(pos->member.next, typeof(*pos), member))/*** list_for_each_entry_safe - iterate over list of given type safe against removal of list entry* @pos: the type * to use as a loop cursor.* @n: another type * to use as temporary storage* @head: the head for your list.* @member: the name of the list_head within the struct.*/#definelist_for_each_entry_safe(pos, n, head, member) \for(pos = list_entry((head)->next, typeof(*pos), member), \n = list_entry(pos->member.next, typeof(*pos), member); \&pos->member != (head); \pos = n, n = list_entry(n->member.next, typeof(*n), member))/*** list_empty - tests whether a list is empty* @head: the list to test.*/staticinline intlist_empty(conststructlist_head *head){returnhead->next == head;}/** Insert a new entry between two known consecutive entries.** This is only for internal list manipulation where we know* the prev/next entries already!*/staticinline void__list_add(structlist_head *_new,structlist_head *prev,structlist_head *next){next->prev = _new;_new->next = next;_new->prev = prev;prev->next = _new;}/*** 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.*/staticinline voidlist_add_tail(structlist_head *_new, structlist_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!*/staticinline void__list_del(structlist_head *prev, structlist_head *next){next->prev = prev;prev->next = next;}#defineLIST_POISON1 ((void *) 0x00100100)#defineLIST_POISON2 ((void *) 0x00200200)/*** 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.*/staticinline voidlist_del(structlist_head *entry){__list_del(entry->prev, entry->next);entry->next = (structlist_head*)LIST_POISON1;entry->prev = (structlist_head*)LIST_POISON2;}#endif
test.c
#include
代码输出
讨论的重点是?
如下图
因为Linux内核用的是C89标准,不能在for循环里面声明变量,所以导致tmp变量在使用之后的代码中还可以继续使用。
继续使用并不是大问题,大问题是因为继续使用导致了一个USB的BUG,当然,从代码的结构性上来说,我觉得也应该做好封装。
根据这个机制,有可能会被程序攻击到内核代码
具体可以查看这个网址
https://www.vusec.net/projects/kasper/
里面的描述和补丁说明差不多,都是因为没有遍历结束退出的原因。
修改后的部分补丁
+/* Override the default implementation from linux/nospec.h. */+#define select_nospec(cond, exptrue, expfalse) \+({ \+ typeof(exptrue) _out= (exptrue); \+ \+ asmvolatile("test %1, %1\n\t"\+ "cmove %2, %0"\+ : "+r"(_out) \+ : "r"(cond), "r"(expfalse)); \+ _out; \+})+/* Prevent speculative execution past this barrier. */#define barrier_nospec() alternative("", "lfence", X86_FEATURE_LFENCE_RDTSC)diff --git a/include/linux/list.h b/include/linux/list.hindex dd6c2041d09c..1a1b39fdd122 100644--- a/include/linux/list.h+++ b/include/linux/list.h@@ -636,7+636,8@@ staticinlinevoidlist_splice_tail_init(structlist_head *list,*/#define list_for_each_entry(pos, head, member) \for(pos = list_first_entry(head, typeof(*pos), member); \- !list_entry_is_head(pos, head, member); \+ ({ bool_cond = !list_entry_is_head(pos, head, member); \+ pos = select_nospec(_cond, pos, NULL); _cond; }); \pos = list_next_entry(pos, member))
具体网址:
https://lwn.net/ml/linux-kernel/20220217184829.1991035-2-jakobkoschel@gmail.com/
相关阅读
-
世界热推荐:今晚7:00直播丨下一个突破...
今晚19:00,Cocos视频号直播马上点击【预约】啦↓↓↓在运营了三年... -
NFT周刊|Magic Eden宣布支持Polygon网...
Block-986在NFT这样的市场,每周都会有相当多项目起起伏伏。在过去... -
环球今亮点!头条观察 | DeFi的兴衰与...
在比特币得到机构关注之后,许多财务专家预测世界将因为加密货币的... -
重新审视合作,体育Crypto的可靠关系才能双赢
Block-987即使在体育Crypto领域,人们的目光仍然集中在FTX上。随着... -
简讯:前端单元测试,更进一步
前端测试@2022如果从2014年Jest的第一个版本发布开始计算,前端开发... -
焦点热讯:刘强东这波操作秀
近日,刘强东发布京东全员信,信中提到:自2023年1月1日起,逐步为...