当前位置: 首页 > 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…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...