Linux Kernel Stack Overflow/Linux 内核栈溢出

不同于Linux应用程序的栈能够动态增长,Linux内核栈是固定的,并且比较小,比如Linux 2.6.x内核,在X86 32位架构上一般是4K或8K(在进行内核编译时,Kernel hacking下进行配置,默认8K),而在X86 64位架构上固定为8K。Linux内核会分配一页(4K stack)或两页连续(8K stack)不可交换(non-swappable)内存来作为内核栈使用。Linux 2.4.x内核在X86位架构上,内核栈固定为8K。

当一个进行运行在内核态时(比如通过系统调用),它就将开始使用它自己的内核栈,如果内核栈大小为8K,那么此时的触发的中断处理也将使用这个栈,如果内核栈大小为4K,那么此时的触发的中断处理则将使用单独的内核栈。

由于内核栈大小固定且比较小,很容易出现内核栈溢出的情况,所以不能在内核代码里使用递归调用(除非你非常清楚它递归的层次,但仍建议将递归改为循环,因为谁也不知道将来哪一天递归的层次是否会发生变化),也不建议使用较大或大小未知的栈变量(比如动态数组)等。

由于task_struct和内核栈共用同一块内存区域,所以内核栈溢出最直接的后果就是把task_struct结构体踩坏,在linux下,这个结构体是至关重要的,每一个进程都是由这个task_struct数据结构来定义,它也就是我们通常所说的PCB,它是用来对进程进行控制的唯一手段,也是最有效的手段;但这是kernel 2.4.x的情况,下面代码来之kernel 2.4.37.11:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

union task_union {

    struct task_struct task;

    unsigned long stack[INIT_TASK_SIZE/sizeof(long)];

};

struct task_struct {

    /*

     * offsets of these are hardcoded elsewhere - touch with care

     */

    volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */

    unsigned long flags;    /* per process flags, defined below */

    int sigpending;

    mm_segment_t addr_limit;    /* thread address space:

                        0-0xBFFFFFFF for user-thead

                        0-0xFFFFFFFF for kernel-thread

                     */

    struct exec_domain *exec_domain;

    volatile long need_resched;

    unsigned long ptrace;

    int lock_depth;     /* Lock depth */

...

};

到了kernel 2.6.x之后,和内核栈共用同一块内存区域的不再是task_struct,而是结构体thread_info,下面代码来之kernel 2.6.38.8:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

union thread_union {

    struct thread_info thread_info;

    unsigned long stack[THREAD_SIZE/sizeof(long)];

};

struct thread_info {

    struct task_struct  *task;      /* main task structure */

    struct exec_domain  *exec_domain;   /* execution domain */

    __u32           flags;      /* low level flags */

    __u32           status;     /* thread synchronous flags */

    __u32           cpu;        /* current CPU */

    int         preempt_count;  /* 0 => preemptable,

                           <0 => BUG */

    mm_segment_t        addr_limit;

    struct restart_block    restart_block;

    void __user     *sysenter_return;

#ifdef CONFIG_X86_32

    unsigned long           previous_esp;   /* ESP of the previous stack in

                           case of nested (IRQ) stacks

                        */

    __u8            supervisor_stack[0];

#endif

    int         uaccess_err;

};

不过由于thread_info结构体的第一个字段就是task_struct指针,所以内核栈溢出的话同样会损坏task_struct,因为此时task指针指向一个不可预知的地址,相应的task_struct结构体各个字段数据当然也就都是垃圾数据了。

看图示更直观:


一旦内核栈溢出,间接导致task_struct结构体里的数据异常,那么就会导致系统处于一种不稳定状态(当然,一般情况也就是宕机):


当在查宕机问题时,如果定位到是由于thread_info结构体或task_struct结构体里数据异常导致(比如引起系统宕机的指令是访问task_struct结构体变量的某个字段),那么就要优先考虑是否由内核栈下溢引起宕机。对于栈的保护,Linux内核提供了一系列选项,比如DEBUG_STACKOVERFLOW、CC_STACKPROTECTOR等,但这些都只是辅助手段,要如何定位到更具体的位置呢?内核函数对栈空间的预留和应用层没有什么两样,同样是移动esp或rsp,所以我们可以先找出所有这些预留的地方,看哪个地方预留得最多,也就是该函数占用的栈空间最多,那么就是最有可能引发内核栈溢出的地方。
示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

/**

 * kernel_stack.c

 */

#include<linux/kernel.h>

#include<linux/types.h>

#include<linux/stddef.h>

#include<linux/module.h>

#define NUM 256

static int just_copy_half(int *array, int count)

{

    int i;

    int another_array[count];

    for (i = 0; i < count; i ++) {

        another_array[i] = array[i];

    }

    return 0;

}

static int __init kernel_stack_init(void)

{

    int ret = 0;

    int total = 0;

    int array[NUM];

    int i;

    for (i = 0; i < NUM; i ++)

        array[i] = i;

         

    for (i = 0; i < NUM; i ++)

        total += array[i];

         

    just_copy_half(array, NUM/2);

         

    printk("Total: %d\n", total);

    return ret;

}

static void __exit kernel_stack_fini(void)

{

    //Do Nothing

    return;

}

module_init(kernel_stack_init);

module_exit(kernel_stack_fini);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("lenky0401 at gmail dot com");

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

# Makefile

MDIR = $(shell pwd)

ifeq (, $(KSRC))

    KSRC := /usr/src/linux-2.6.36

endif

ifeq (, $(PROJECT_DIR))

    PROJECT_DIR := $(PWD)/../

endif

module := kernel_stack

obj-m := $(module).o

srcs =  $(wildcard, *.c)

$(module)-objs := $(addsuffix .o, $(basename $(srcs)))

EXTRA_CFLAGS += $(FLAG) -I$(PROJECT_DIR)/inc -I${SHAREDHDR} -I$(KERNELHDR) -O2 -D__KERNEL__ -DMODULE $(INCLUDE) -DEXPORT_SYMTAB

TARGET = $(module).ko

all:

    make -C $(KSRC) M=$(MDIR) modules

debug:

    make EXTRA_FLAGS="${EXTRA_CFLAGS} -DDEBUG" -C $(KSRC) M=$(MDIR) modules

clean:

    make -C $(KSRC) M=$(MDIR) clean

install: all

    cp -f $(TARGET) $(INSTALL_DIR)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

[root@localhost kernel_stack]# ls

kernel_stack.c  Makefile

[root@localhost kernel_stack]# make

make -C /usr/src/linux-2.6.36 M=/home/lenky/modules/kernel_stack modules

make[1]: Entering directory `/usr/src/linux-2.6.36'

  CC [M]  /home/lenky/modules/kernel_stack/kernel_stack.o

  Building modules, stage 2.

  MODPOST 1 modules

  CC      /home/lenky/modules/kernel_stack/kernel_stack.mod.o

  LD [M]  /home/lenky/modules/kernel_stack/kernel_stack.ko

make[1]: Leaving directory `/usr/src/linux-2.6.36'

[root@localhost kernel_stack]# ls

kernel_stack.c   kernel_stack.mod.c  kernel_stack.o  modules.order

kernel_stack.ko  kernel_stack.mod.o  Makefile        Module.symvers

[root@localhost kernel_stack]# objdump --source kernel_stack.ko > kernel_stack.s

[root@localhost kernel_stack]# cat kernel_stack.s | grep sub | grep rsp

  13:   48 29 c4                sub    %rax,%rsp

   3:   48 81 ec 00 04 00 00    sub    $0x400,%rsp

可以看到,一个地方(kernel_stack_init函数)的栈占去$0x400(有几个局部变量是直接使用的寄存器,所以才没有消耗栈),而另外一个地方(just_copy_half函数)的栈占去%rax是个不定值,这就需要继续看对应的汇编来进行分析(因为数组是动态数组),这只是个示例,如果在真实内核代码中有这样的函数,那是相当危险的。更多细节不说,相关汇编代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

kernel_stack.ko:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <just_copy_half>:

static int just_copy_half(int *array, int count)

{

    int i;

    int another_array[count];

   0:   48 63 c6                movslq %esi,%rax

   3:   55                      push   %rbp

   4:   48 8d 04 85 1e 00 00    lea    0x1e(,%rax,4),%rax

   b:   00

   c:   48 89 e5                mov    %rsp,%rbp

   f:   48 83 e0 f0             and    $0xfffffffffffffff0,%rax

  13:   48 29 c4                sub    %rax,%rsp

  16:   4c 8d 44 24 0f          lea    0xf(%rsp),%r8

  1b:   49 83 e0 f0             and    $0xfffffffffffffff0,%r8

    for (i = 0; i < count; i ++) {

  1f:   85 f6                   test   %esi,%esi

  21:   7e 16                   jle    39 <just_copy_half+0x39>

  23:   31 c9                   xor    %ecx,%ecx

  25:   31 d2                   xor    %edx,%edx

        another_array[i] = array[i];

  27:   8b 04 97                mov    (%rdi,%rdx,4),%eax

  2a:   83 c1 01                add    $0x1,%ecx

  2d:   41 89 04 90             mov    %eax,(%r8,%rdx,4)

  31:   48 83 c2 01             add    $0x1,%rdx

  35:   39 f1                   cmp    %esi,%ecx

  37:   75 ee                   jne    27 <just_copy_half+0x27>

    }

    return 0;

}

  39:   c9                      leaveq

  3a:   31 c0                   xor    %eax,%eax

  3c:   c3                      retq  

  3d:   00 00                   add    %al,(%rax)

    ...

Disassembly of section .exit.text:

0000000000000000 <cleanup_module>:

static int __init kernel_stack_init(void)

{

    int ret = 0;

    int total = 0;

    int array[NUM];

    int i;

    for (i = 0; i < NUM; i ++)

        array[i] = i;

         

    for (i = 0; i < NUM; i ++)

        total += array[i];

         

    just_copy_half(array, NUM/2);

         

    printk("Total: %d\n", total);

    return ret;

}

static void __exit kernel_stack_fini(void)

{

    //Do Nothing

    return;

}

   0:   f3 c3                   repz retq

Disassembly of section .init.text:

0000000000000000 <init_module>:

   0:   53                      push   %rbx

   1:   31 c0                   xor    %eax,%eax

   3:   48 81 ec 00 04 00 00    sub    $0x400,%rsp

   a:   48 89 e7                mov    %rsp,%rdi

   d:   0f 1f 00                nopl   (%rax)

  10:   89 04 87                mov    %eax,(%rdi,%rax,4)

  13:   48 83 c0 01             add    $0x1,%rax

  17:   48 3d 00 01 00 00       cmp    $0x100,%rax

  1d:   75 f1                   jne    10 <init_module+0x10>

  1f:   31 db                   xor    %ebx,%ebx

  21:   66 31 c0                xor    %ax,%ax

  24:   03 1c 87                add    (%rdi,%rax,4),%ebx

  27:   48 83 c0 01             add    $0x1,%rax

  2b:   48 3d 00 01 00 00       cmp    $0x100,%rax

  31:   75 f1                   jne    24 <init_module+0x24>

  33:   48 89 e7                mov    %rsp,%rdi

  36:   be 80 00 00 00          mov    $0x80,%esi

  3b:   e8 00 00 00 00          callq  40 <init_module+0x40>

  40:   89 de                   mov    %ebx,%esi

  42:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi

  49:   31 c0                   xor    %eax,%eax

  4b:   e8 00 00 00 00          callq  50 <init_module+0x50>

  50:   48 81 c4 00 04 00 00    add    $0x400,%rsp

  57:   31 c0                   xor    %eax,%eax

  59:   5b                      pop    %rbx

  5a:   c3                      retq  

上面提到Linux 2.6.x内核在X86 32位架构上可以配置内核栈大小(在进行内核编译时,Kernel hacking下进行配置,默认8K,配置之后对应的宏为CONFIG_4KSTACKS),具体生效代码可以看:
LXR / The Linux Cross Reference

1

2

3

4

5

6

#ifdef CONFIG_4KSTACKS

#define THREAD_ORDER    0

#else

#define THREAD_ORDER    1

#endif

#define THREAD_SIZE     (PAGE_SIZE << THREAD_ORDER)

通过CONFIG_4KSTACKS宏来定义THREAD_SIZE大小,但是在2.6.37后的内核代码里都已经找不到这个定义了:
LXR / The Linux Cross Reference
LXR / The Linux Cross Reference
LXR / The Linux Cross Reference
后来一查才知道CONFIG_4KSTACKS已经被移除了:https://lkml.org/lkml/2010/6/29/107,好吧,继续8K内核栈。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/618306.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

上门回收小程序开发,让回收更加简单

资源回收一直是当下深受大众关注的话题&#xff0c;如何做到资源不浪费&#xff0c;成为了大众要考虑的问题。在人们环保意识的加深下&#xff0c;回收行业也是获得了大众的关注&#xff0c;逐渐形成了一个新的商业模式。 随着互联网技术的发展&#xff0c;回收行业也更加方便…

C/S架构,集成三维影像后处理功能,自主版权的一套医院PACS系统源码

一、PACS简介 PACS&#xff08;PictureArchivingandCommunicationsSystem&#xff09;即图像存储与传输系统&#xff0c;是应用于医院的数字医疗设备如CT、MR&#xff08;磁共振&#xff09;、US&#xff08;超声成像&#xff09;、X光机、DSA&#xff08;数字减影&#xff09…

KIBANA可视化管理界面说明

更说明转自https://blog.csdn.net/IT_ZRS/article/details/125496588 1 主要结构功能 使用浏览器访问 ip:5601 默认端口&#xff0c;进入首页 Discover&#xff1a;日志管理视图 主要进行搜索和查询Visualize&#xff1a;统计视图 构建可视化的图表Dashboard&#xf…

【目标检测】YOLOv5算法实现(八):模型验证

本系列文章记录本人硕士阶段YOLO系列目标检测算法自学及其代码实现的过程。其中算法具体实现借鉴于ultralytics YOLO源码Github&#xff0c;删减了源码中部分内容&#xff0c;满足个人科研需求。   本系列文章主要以YOLOv5为例完成算法的实现&#xff0c;后续修改、增加相关模…

JavaWeb,CSS的学习

CSS&#xff0c;层叠样式表&#xff08;Cascading Style Sheets&#xff09;&#xff0c;能够对网页中元素位置的排版进行像素级精确控制&#xff0c;支持几乎所有的字体字号样式&#xff0c;拥有网页对象和模型样式编辑的能力&#xff0c;简单来说&#xff0c;美化页面。 CSS…

c++临时对象的探讨及相关性能提升

产生临时对象的情况 我们定义一个类进行测试 class tempVal { public:int v1, v2;tempVal(int v1 0, int v2 0);tempVal(const tempVal& t) :v1(t.v1), v2(t.v2) {cout << "调用拷贝构造函数" << endl;}virtual ~tempVal() {cout << "…

【python】——turtle动态画

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

AR HUD全面「上新」

AR HUD赛道正在迎来新的时代。 上周&#xff0c;蔚来ET9正式发布亮相&#xff0c;新车定位为D级行政旗舰轿车&#xff0c;其中&#xff0c;在智能座舱交互层面&#xff0c;继理想L系列、长安深蓝S7之后&#xff0c;也首次取消仪表盘&#xff0c;取而代之的是业内首个全焦段AR H…

分块矩阵的定义、计算

目录 一、定义 二、分块矩阵的加减乘法 三、考点 一、定义 分块&#xff0c;顾名思义&#xff0c;将整个矩阵分成几部分&#xff0c;如下图所示 二、分块矩阵的加减乘法 三、考点 分块矩阵的考点不多&#xff0c;一般来说&#xff0c;有一种&#xff1a; 求分块矩阵的转置…

PHP如何拆分中文名字(包括少数民族名字)

/*** param string|null $name* return array|null*/ function splitName($name) {if (empty($name) || empty(trim($name))) {return null;}//该正则是用来提取$name参数里面的中文字符的。preg_match_all(/[\x{4e00}-\x{9fff}]/u, $name, $matchers);$matchersCount isset($…

2024年,谷歌云首席技术官眼中的生成AI三大支柱,来看看有啥新花样

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

App在线封装的革命性创新

随着移动互联网的蓬勃发展&#xff0c;App已经成为我们日常生活中不可或缺的一部分。从购物、交通、社交到娱乐&#xff0c;几乎每个人的智能手机都装载着数十个应用程序&#xff0c;以满足各式各样的需求。然而&#xff0c;对于许多非技术背景的企业家和小型企业而言&#xff…

【机器学习】模型参数优化工具:Optuna使用分步指南(附XGB/LGBM调优代码)

常用的调参方式和工具包 常用的调参方式包括网格搜索(Grid Search)、**随机搜索(Random Search)和贝叶斯优化(Bayesian Optimization)**等。 工具包方面&#xff0c;Scikit-learn提供了GridSearchCV和RandomizedSearchCV等用于网格搜索和随机搜索的工具。另外&#xff0c;有一…

VS报错:error:LNK2005 _main 已经在 *.obj 中定义

应该是重定义了&#xff0c;但是又解决不了&#xff0c;看似又没有重定义啊&#xff0c;就在一个文件定义了啊&#xff1f;怎么会出现这种情况呢&#xff1f;关键是&#xff0c;编译报错&#xff0c;程序运行不了了。 这里提一下我的前期操作&#xff0c;是因为将一个头文件和…

NULL是什么?

NULL是一个编程术语&#xff0c;通常用于表示一个空值或无效值。在很多编程语言中&#xff0c;NULL用于表示一个变量或指针不引用任何有效的对象或内存位置。 NULL可以看作是一个特殊的值&#xff0c;表示缺少有效的数据或引用。当一个变量被赋予NULL值时&#xff0c;它表示该变…

【面试宝典】图解ARP协议、TCP协议、UDP协议

一、ARP协议 二、TCP协议 三、UDP协议 四、TCP和UDP的区别

如何有效使用360评估

导语&#xff1a;360度评估是绩效考核方法之一&#xff0c;被评估者不仅可以从自己、上司、部属、甚至顾客处获得多种角度的反馈&#xff0c;也可从这些不同的反馈清楚地认识到自己的不足、长处与发展需求。但360度评估也有其适用的范围和条件&#xff0c;华为总裁任正非给出了…

impala元数据自动刷新

一.操作步骤 进入CM界面 > Hive > 配置 > 搜索 启用数据库中的存储通知(英文界面搜索&#xff1a;Enable Stored Notifications in Database)&#xff0c;并且勾选&#xff0c;注意一定要勾选&#xff0c;配置后面的配置不生效。数据库通知的保留时间默认为2天&#…

Nacos下载与安装【windows】

&#x1f95a;今日鸡汤&#x1f95a; 我不知将去何方&#xff0c;但我已经在路上。 ——宫崎骏《千与千寻》 目录 &#x1f95e;1.Nacosdi地址 &#x1f32d;2.GitHub下载 &#x1f37f;3.目录结构 &#x1f953;4.启动nacos &#x1f9c2;5.客户端登陆 &#x1f9c8…

GitHub 一周热点汇总第5期(2024/01/07-01/13)

GitHub一周热点汇总第5期 (2024/01/07-01/13)&#xff0c;梳理每周热门的GitHub项目&#xff0c;这一周的热门项目都普遍比较年轻&#xff0c;有几个更是刚刚发布就火热起来了&#xff0c;一起来看看都有哪些项目吧。 #1 llm-course 项目名称&#xff1a;llm-course - 大语言…