当前位置: 首页 > news >正文

kernel源码分析 do_msgsnd read_msg

 笔者分析的源码是v 5.11.22

链接:msg.c - ipc/msg.c - Linux source code v5.11.22 - Bootlin

do_msgsnd

static long do_msgsnd(int msqid, long mtype, void __user *mtext,size_t msgsz, int msgflg)
{struct msg_queue *msq;struct msg_msg *msg;int err;struct ipc_namespace *ns;DEFINE_WAKE_Q(wake_q);ns = current->nsproxy->ipc_ns;if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)return -EINVAL;if (mtype < 1)return -EINVAL;msg = load_msg(mtext, msgsz);if (IS_ERR(msg))return PTR_ERR(msg);msg->m_type = mtype;msg->m_ts = msgsz;rcu_read_lock();msq = msq_obtain_object_check(ns, msqid);if (IS_ERR(msq)) {err = PTR_ERR(msq);goto out_unlock1;}ipc_lock_object(&msq->q_perm);for (;;) {struct msg_sender s;err = -EACCES;if (ipcperms(ns, &msq->q_perm, S_IWUGO))goto out_unlock0;/* raced with RMID? */if (!ipc_valid_object(&msq->q_perm)) {err = -EIDRM;goto out_unlock0;}err = security_msg_queue_msgsnd(&msq->q_perm, msg, msgflg);if (err)goto out_unlock0;if (msg_fits_inqueue(msq, msgsz))break;/* queue full, wait: */if (msgflg & IPC_NOWAIT) {err = -EAGAIN;goto out_unlock0;}/* enqueue the sender and prepare to block */ss_add(msq, &s, msgsz);if (!ipc_rcu_getref(&msq->q_perm)) {err = -EIDRM;goto out_unlock0;}ipc_unlock_object(&msq->q_perm);rcu_read_unlock();schedule();rcu_read_lock();ipc_lock_object(&msq->q_perm);ipc_rcu_putref(&msq->q_perm, msg_rcu_free);/* raced with RMID? */if (!ipc_valid_object(&msq->q_perm)) {err = -EIDRM;goto out_unlock0;}ss_del(&s);if (signal_pending(current)) {err = -ERESTARTNOHAND;goto out_unlock0;}}ipc_update_pid(&msq->q_lspid, task_tgid(current));msq->q_stime = ktime_get_real_seconds();if (!pipelined_send(msq, msg, &wake_q)) {/* no one is waiting for this message, enqueue it */list_add_tail(&msg->m_list, &msq->q_messages);msq->q_cbytes += msgsz;msq->q_qnum++;atomic_add(msgsz, &ns->msg_bytes);atomic_inc(&ns->msg_hdrs);}err = 0;msg = NULL;out_unlock0:ipc_unlock_object(&msq->q_perm);wake_up_q(&wake_q);
out_unlock1:rcu_read_unlock();if (msg != NULL)free_msg(msg);return err;
}

首先进行判断

if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)return -EINVAL;
if (mtype < 1)return -EINVAL;

合法的msqid都是大于等于0的,这个不用管,需要注意的是mtype<1是invalid argument,所以在创建msg_msg时,要确保mtype>=1。

接下来

msg = load_msg(mtext, msgsz);
struct msg_msg *load_msg(const void __user *src, size_t len)
{struct msg_msg *msg;struct msg_msgseg *seg;int err = -EFAULT;size_t alen;msg = alloc_msg(len);if (msg == NULL)return ERR_PTR(-ENOMEM);alen = min(len, DATALEN_MSG);if (copy_from_user(msg + 1, src, alen))goto out_err;for (seg = msg->next; seg != NULL; seg = seg->next) {len -= alen;src = (char __user *)src + alen;alen = min(len, DATALEN_SEG);if (copy_from_user(seg + 1, src, alen))goto out_err;}err = security_msg_msg_alloc(msg);if (err)goto out_err;return msg;out_err:free_msg(msg);return ERR_PTR(err);
}

 

static struct msg_msg *alloc_msg(size_t len)
{struct msg_msg *msg;struct msg_msgseg **pseg;size_t alen;alen = min(len, DATALEN_MSG);msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT);if (msg == NULL)return NULL;msg->next = NULL;msg->security = NULL;len -= alen;pseg = &msg->next;while (len > 0) {struct msg_msgseg *seg;cond_resched();alen = min(len, DATALEN_SEG);seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);if (seg == NULL)goto out_err;*pseg = seg;seg->next = NULL;pseg = &seg->next;len -= alen;}return msg;out_err:free_msg(msg);return NULL;
}
struct msg_msgseg {struct msg_msgseg *next;/* the next part of the message follows immediately */
};#define DATALEN_MSG	((size_t)PAGE_SIZE-sizeof(struct msg_msg))
#define DATALEN_SEG	((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))

总的来说,就是分配我们传入的bufsz大小的msg_msg,然后将mtext给copy过去,注意,我们这里的msg->security==NULL。

struct msg_msg {struct list_head m_list;long m_type;size_t m_ts;		/* message text size */struct msg_msgseg *next;void *security;/* the actual message follows immediately */
};
struct msg_queue {struct kern_ipc_perm q_perm;time64_t q_stime;		/* last msgsnd time */time64_t q_rtime;		/* last msgrcv time */time64_t q_ctime;		/* last change time */unsigned long q_cbytes;		/* current number of bytes on queue */unsigned long q_qnum;		/* number of messages in queue */unsigned long q_qbytes;		/* max number of bytes on queue */struct pid *q_lspid;		/* pid of last msgsnd */struct pid *q_lrpid;		/* last receive pid */struct list_head q_messages;struct list_head q_receivers;struct list_head q_senders;
} __randomize_layout;
list_add_tail(&msg->m_list, &msq->q_messages);

具体将msg_msg链入的一部是这个,list_add_tail

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{__list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{next->prev = new;new->next = next;new->prev = prev;prev->next = new;
}

我们也可以知道msgq中的一些字段的含义

msq->q_cbytes += msgsz;
msq->q_qnum++;

q_cbtytes字段就是这个queue中存储的总字节数

q_qnum就是拥有的msg_msg的数量。

从kernel pwn的角度来说,能用的就是load_msg那里,先用alloc_msg构建好msg_msg,所有next指针都分配好了,然后再copy,可以用userfault卡在copy_from_user,然后修改next指针实现地址任意写。

接下来我们分析msg_read 

msg_read

很多细节前面的msgrcv分析copy都讲了,这里主要讲不一样的。

static inline int convert_mode(long *msgtyp, int msgflg)
{if (msgflg & MSG_COPY)return SEARCH_NUMBER;/**  find message of correct type.*  msgtyp = 0 => get first.*  msgtyp > 0 => get first message of matching type.*  msgtyp < 0 => get message with least type must be < abs(msgtype).*/if (*msgtyp == 0)return SEARCH_ANY;if (*msgtyp < 0) {if (*msgtyp == LONG_MIN) /* -LONG_MIN is undefined */*msgtyp = LONG_MAX;else*msgtyp = -*msgtyp;return SEARCH_LESSEQUAL;}if (msgflg & MSG_EXCEPT)return SEARCH_NOTEQUAL;return SEARCH_EQUAL;
}

我们的msgtpy是0,所以mode就是SEARCH_EQUAL

static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
{struct msg_msg *msg, *found = NULL;long count = 0;list_for_each_entry(msg, &msq->q_messages, m_list) {if (testmsg(msg, *msgtyp, mode) &&!security_msg_queue_msgrcv(&msq->q_perm, msg, current,*msgtyp, mode)) {if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) {*msgtyp = msg->m_type - 1;found = msg;} else if (mode == SEARCH_NUMBER) {if (*msgtyp == count)return msg;} elsereturn msg;count++;}}return found ?: ERR_PTR(-EAGAIN);
}
static int testmsg(struct msg_msg *msg, long type, int mode)
{switch (mode) {case SEARCH_ANY:case SEARCH_NUMBER:return 1;case SEARCH_LESSEQUAL:if (msg->m_type <= type)return 1;break;case SEARCH_EQUAL:if (msg->m_type == type)return 1;break;case SEARCH_NOTEQUAL:if (msg->m_type != type)return 1;break;}return 0;
}

可以看到,最后会找到与我们传入的type相等的msg_msg

if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {msg = ERR_PTR(-E2BIG);goto out_unlock0;}

我们传入的msgflg是0,所以后面是真,也就是说,如果我们传入的bufsz<msg->m_ts,是没用的,所以我们传入的bufsz必须大于或等于msg->m_ts。

接下来会进入这个函数。

list_del(&msg->m_list);
static inline void list_del(struct list_head *entry)
{__list_del_entry(entry);entry->next = LIST_POISON1;entry->prev = LIST_POISON2;
}
static inline void __list_del_entry(struct list_head *entry)
{if (!__list_del_entry_valid(entry))return;__list_del(entry->prev, entry->next);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{next->prev = prev;WRITE_ONCE(prev->next, next);
}
static inline bool __list_del_entry_valid(struct list_head *entry)
{return true;
}

其实就是脱链操作,从kernel pwn的角度,_list_del_entry_valid是直接返回true,这意味着,在这个版本,我们只需随意把这两个指针覆盖为有效可写地址就行了,或许可以构造unlink攻击?

话说其他版本会不会在此增加一些验证??? 

其他和前篇的msgrcv相似,就不说了。

相关文章:

kernel源码分析 do_msgsnd read_msg

笔者分析的源码是v 5.11.22 链接&#xff1a;msg.c - ipc/msg.c - Linux source code v5.11.22 - Bootlin do_msgsnd static long do_msgsnd(int msqid, long mtype, void __user *mtext,size_t msgsz, int msgflg) {struct msg_queue *msq;struct msg_msg *msg;int err;str…...

掌握 CTE 技巧,实现连续日期和月份的 SQL 报表统计

在 SQL 查询中&#xff0c;报表统计往往涉及到特定时间段内的数据汇总&#xff0c;如每日、每月的销售数据等。然而&#xff0c;面对缺少数据的日期或月份&#xff0c;传统 SQL 查询可能会直接跳过这些日期&#xff0c;使得输出的报表在视觉上并不连续。本文将展示如何利用 CTE…...

【表格解决问题】EXCEL行数过多,WPS如何按逐行分别打印多个纸张中

1 问题描述 如图&#xff1a;我的表格行数太多了。打印在一张纸上有点不太好看 2 解决方式 Step01&#xff1a;先选中你需要打印的部分&#xff0c;找到【页面】->【打印区域】->【设置打印区域】 Step02&#xff1a;先选中一行&#xff0c;找到【插入分页符】 Step0…...

Maven讲解从基础到高级配置与实践

一、基础认知 1.1 Maven 的主要作用 Maven 主要是用来管理 Java 项目构建流程的工具&#xff0c;包括以下几个方面&#xff1a; 依赖管理&#xff1a;通过 POM.xml 文件管理项目的外部依赖库&#xff0c;不同版本的依赖包可以通过 Maven 中央仓库自动下载&#xff0c;减少了…...

Vue3组件式父子传值

下面是使用 <script setup> 语法的 Vue 3 组件之间传值的示例。 示例 1:使用 Props 和 Emits 父组件 <template><div><h1>父组件</h1><ChildComponent :message="parentMessage" @reply="handleReply" /><p>…...

网页自动化测试和爬虫:Selenium库入门与进阶

网页自动化测试和爬虫&#xff1a;Selenium库入门与进阶 在现代Web开发和数据分析中&#xff0c;自动化测试和数据采集成为了开发流程中的重要部分。Python 的 Selenium 库是一种强大的工具&#xff0c;不仅用于网页自动化测试&#xff0c;也在网页爬虫中得到了广泛的应用。本…...

Cells 单元

Goto Data Grid 数据网格 Cells 单元 Content Alignment 内容对齐 显示数值的数据网格单元格会将其内容向右对齐。显示其他类型数据的单元格将其内容向左排列。若要更改单元格内容对齐方式&#xff0c;请处理 ColumnView.RowCellDefaultAlignment 事件。 Selection Modes 选…...

2024/11/2 安卓创建首页界面

‌Gradle 8.7 bin‌是指Gradle 8.7版本的二进制包&#xff0c;通常以.zip或.tar.gz格式提供。这个二进制包包含了运行Gradle所需的所有文件&#xff0c;用户可以直接下载并解压使用&#xff0c;无需从源代码编译。 首先了解最常用的布局 线性布局&#xff08;从上到下&#x…...

SpringSession源码分析

默认对常规Session的理解和使用&#xff0c;如何使用Set-Cookie。 Maven库 常见的spring-session-data-redis依赖spring-session-core <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-core</artifactId&…...

IIC

IIC 目录 IIC BH1750型号的光照传感器 IIC通信协议 iic物理层 IIC软件层协议 -- 那么一主多从&#xff0c;怎么选中与指定的从机通信呢&#xff1f; 从机设备地址 -- 从手册中查看 IIC 写操作 IIC 读操作 硬件IIC和模拟 IIC 使用 模拟 IIC 使用 &#xff01;&…...

LLM Observability: Azure OpenAI (一)

作者&#xff1a;来自 Elastic Vinay Chandrasekhar•Andres Rodriguez 我们很高兴地宣布 Azure OpenAI 集成现已全面上市&#xff0c;它提供了对 Azure OpenAI 服务性能和使用的全面可观察性&#xff01;另请参阅本博客的第 2 部分 虽然我们已经提供了对 LLM 环境的可视性一段…...

qt QBrush详解

1、概述 QBrush是Qt框架中的一个基本图形对象类&#xff0c;它主要用于定义图形的填充模式。QBrush可以用于填充如矩形、椭圆形、多边形等形状&#xff0c;也可以用于绘制背景等。通过QBrush&#xff0c;可以设置填充的颜色、样式&#xff08;如实心、渐变、纹理等&#xff09…...

Excel函数CUnique连接合并指定区域的唯一值

上一篇文章向大家介绍了如何使用VBA在低版本Excel中创建unique函数的方法&#xff0c;今天我跟大家分享一下如何使用函数连接指定区域的唯一值&#xff0c;也就是将unique函数获取的唯一值连接合并成一个&#xff0c;并指定连接符。 同样&#xff0c;我们需要先创建一个自定义的…...

机械革命屏幕设置为RGB

机械革命屏幕设置为RGB 如何设为机械革命屏幕显示为RGB如何设置1.win菜单下输入“显卡控制中心”2.选择显示器3.设置为RGB4.饱和度大家设为自己舒服的就行5.调整亮度 参考来源 如何设为机械革命屏幕显示为RGB 之前买的显示器&#xff0c;感觉调成sRGB看起来非常舒服。就想着是…...

开源项目-投票管理系统

哈喽,大家好,今天主要给大家带来一个开源项目-投票管理系统 投票管理系统主要有首页,发起投票,管理投票,参与投票,查看投票等功能 首页 为用户提供了一键导航到各个功能模块的便捷途径。 新增投票 用户可以在此轻松创建新的投票活动,设置投票主题、选项等信息。 管理…...

LeetCode 104.二叉树的最大深度

题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1a;root [1…...

Android启动流程_Init阶段

前言 本文将会介绍 Android 启动流程&#xff0c;将基于 Android 10 代码逻辑介绍原生启动过程。 bootloader 上电 -> 加载 recovery 镜像或者 boot 镜像 -> linux kernel 启动 -> 加载 init 进程 -> 加载 zygote 进程 -> systemserver 进程 -> 系统启动 …...

萤火虫算法优化BILSTM神经网络多输入回归分析

目录 LSTM的基本定义 LSTM实现的步骤 BILSTM神经网络 代码 结果分析 展望 完整代码下载:的MATALB代码(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88755564 背影 bp神经网络是一种成熟的神经网络,应用非常广,本文用萤火虫算法…...

在线QP(QuotedPrintable)编码解码工具

具体前往&#xff1a;Quoted-printable在线编码解码工具-将给定文本编码为:可打印字符引用编码(简称&#xff1a;QP编码)&#xff0c;也支持在线解码...

【已解决】cra 配置路径别名 @ 后,出现 ts 报错:找不到模块“@/App”或其相应的类型声明。ts(2307)

cra 配置路径别名 后&#xff0c;出现 ts 报错&#xff1a;找不到模块“/App”或其相应的类型声明。ts(2307) 然后可以在 tsconfig.json 中配置 baseUrl 和 paths &#xff1a; {"compilerOptions": {"target": "es5","lib": [&quo…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例

目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码&#xff1a;冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...

JDK 17 序列化是怎么回事

如何序列化&#xff1f;其实很简单&#xff0c;就是根据每个类型&#xff0c;用工厂类调用。逐个完成。 没什么漂亮的代码&#xff0c;只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...