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 链接: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 查询中,报表统计往往涉及到特定时间段内的数据汇总,如每日、每月的销售数据等。然而,面对缺少数据的日期或月份,传统 SQL 查询可能会直接跳过这些日期,使得输出的报表在视觉上并不连续。本文将展示如何利用 CTE…...
【表格解决问题】EXCEL行数过多,WPS如何按逐行分别打印多个纸张中
1 问题描述 如图:我的表格行数太多了。打印在一张纸上有点不太好看 2 解决方式 Step01:先选中你需要打印的部分,找到【页面】->【打印区域】->【设置打印区域】 Step02:先选中一行,找到【插入分页符】 Step0…...
Maven讲解从基础到高级配置与实践
一、基础认知 1.1 Maven 的主要作用 Maven 主要是用来管理 Java 项目构建流程的工具,包括以下几个方面: 依赖管理:通过 POM.xml 文件管理项目的外部依赖库,不同版本的依赖包可以通过 Maven 中央仓库自动下载,减少了…...
Vue3组件式父子传值
下面是使用 <script setup> 语法的 Vue 3 组件之间传值的示例。 示例 1:使用 Props 和 Emits 父组件 <template><div><h1>父组件</h1><ChildComponent :message="parentMessage" @reply="handleReply" /><p>…...
网页自动化测试和爬虫:Selenium库入门与进阶
网页自动化测试和爬虫:Selenium库入门与进阶 在现代Web开发和数据分析中,自动化测试和数据采集成为了开发流程中的重要部分。Python 的 Selenium 库是一种强大的工具,不仅用于网页自动化测试,也在网页爬虫中得到了广泛的应用。本…...
Cells 单元
Goto Data Grid 数据网格 Cells 单元 Content Alignment 内容对齐 显示数值的数据网格单元格会将其内容向右对齐。显示其他类型数据的单元格将其内容向左排列。若要更改单元格内容对齐方式,请处理 ColumnView.RowCellDefaultAlignment 事件。 Selection Modes 选…...
2024/11/2 安卓创建首页界面
Gradle 8.7 bin是指Gradle 8.7版本的二进制包,通常以.zip或.tar.gz格式提供。这个二进制包包含了运行Gradle所需的所有文件,用户可以直接下载并解压使用,无需从源代码编译。 首先了解最常用的布局 线性布局(从上到下&#x…...
SpringSession源码分析
默认对常规Session的理解和使用,如何使用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软件层协议 -- 那么一主多从,怎么选中与指定的从机通信呢? 从机设备地址 -- 从手册中查看 IIC 写操作 IIC 读操作 硬件IIC和模拟 IIC 使用 模拟 IIC 使用 !&…...
LLM Observability: Azure OpenAI (一)
作者:来自 Elastic Vinay Chandrasekhar•Andres Rodriguez 我们很高兴地宣布 Azure OpenAI 集成现已全面上市,它提供了对 Azure OpenAI 服务性能和使用的全面可观察性!另请参阅本博客的第 2 部分 虽然我们已经提供了对 LLM 环境的可视性一段…...
qt QBrush详解
1、概述 QBrush是Qt框架中的一个基本图形对象类,它主要用于定义图形的填充模式。QBrush可以用于填充如矩形、椭圆形、多边形等形状,也可以用于绘制背景等。通过QBrush,可以设置填充的颜色、样式(如实心、渐变、纹理等)…...
Excel函数CUnique连接合并指定区域的唯一值
上一篇文章向大家介绍了如何使用VBA在低版本Excel中创建unique函数的方法,今天我跟大家分享一下如何使用函数连接指定区域的唯一值,也就是将unique函数获取的唯一值连接合并成一个,并指定连接符。 同样,我们需要先创建一个自定义的…...
机械革命屏幕设置为RGB
机械革命屏幕设置为RGB 如何设为机械革命屏幕显示为RGB如何设置1.win菜单下输入“显卡控制中心”2.选择显示器3.设置为RGB4.饱和度大家设为自己舒服的就行5.调整亮度 参考来源 如何设为机械革命屏幕显示为RGB 之前买的显示器,感觉调成sRGB看起来非常舒服。就想着是…...
开源项目-投票管理系统
哈喽,大家好,今天主要给大家带来一个开源项目-投票管理系统 投票管理系统主要有首页,发起投票,管理投票,参与投票,查看投票等功能 首页 为用户提供了一键导航到各个功能模块的便捷途径。 新增投票 用户可以在此轻松创建新的投票活动,设置投票主题、选项等信息。 管理…...
LeetCode 104.二叉树的最大深度
题目描述 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3 示例 2: 输入:root [1…...
Android启动流程_Init阶段
前言 本文将会介绍 Android 启动流程,将基于 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)编码解码工具
具体前往:Quoted-printable在线编码解码工具-将给定文本编码为:可打印字符引用编码(简称:QP编码),也支持在线解码...
【已解决】cra 配置路径别名 @ 后,出现 ts 报错:找不到模块“@/App”或其相应的类型声明。ts(2307)
cra 配置路径别名 后,出现 ts 报错:找不到模块“/App”或其相应的类型声明。ts(2307) 然后可以在 tsconfig.json 中配置 baseUrl 和 paths : {"compilerOptions": {"target": "es5","lib": [&quo…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...
网页端 js 读取发票里的二维码信息(图片和PDF格式)
起因 为了实现在报销流程中,发票不能重用的限制,发票上传后,希望能读出发票号,并记录发票号已用,下次不再可用于报销。 基于上面的需求,研究了OCR 的方式和读PDF的方式,实际是可行的ÿ…...
使用python进行图像处理—图像滤波(5)
图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值,以达到平滑(去噪)、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算,…...
