[英]Linux Kernel Module unix domain sockets
我正在嘗試在內核和用戶空間之間共享數據。 最終目標是將其移植到 Android,因此我使用的是 unix 域套接字。 (我不知道這是否是最好的選擇)。
我有一個充當套接字客戶端的內核模塊和充當套接字服務器的 ac 程序。 反之亦然,內核模塊充當套接字服務器,ac 程序充當套接字客戶端。
程序非常簡單。 服務器只向客戶端發送一個字符串,然后他們將其打印出來。
我運行server_user.c ,然后嘗試使用 insmod 插入client_module.ko 。 我收到以下錯誤:
Kernel panic - not syncing: stack - protector: Kernel stack is corrupted in: ffffffffa0005157
drm-kms-helper: panic occurred, switching back to text console
怎么了?
模塊服務器
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/un.h>
#include <net/sock.h>
#define SOCK_PATH "/tmp/usocket"
#define LISTEN 10
struct socket *sock = NULL;
struct socket *newsock = NULL;
static int __init server_module_init( void ) {
int retval;
char* string = "hello_world";
struct sockaddr_un addr;
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
// create
retval = sock_create(AF_UNIX, SOCK_STREAM, 0, &sock);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCK_PATH);
// bind
retval = sock->ops->bind(sock,(struct sockaddr *)&addr, sizeof(addr));
// listen
retval = sock->ops->listen(sock, LISTEN);
//accept
retval = sock->ops->accept(sock, newsock, 0);
//sendmsg
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iov->iov_base = string;
msg.msg_iov->iov_len = strlen(string)+1;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
oldfs = get_fs();
set_fs(KERNEL_DS);
retval = sock_sendmsg(newsock, &msg, strlen(string)+1);
set_fs(oldfs);
return 0;
}
static void __exit server_module_exit( void ) {
printk(KERN_INFO "Exit usocket.");
}
module_init( server_module_init );
module_exit( server_module_exit );
MODULE_LICENSE("GPL");
模塊客戶端
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/un.h>
#include <linux/net.h>
#include <net/sock.h>
#include <linux/socket.h>
#define SOCK_PATH "/tmp/usocket"
#define MAX 100
struct socket *sock = NULL;
static int __init client_module_init( void ) {
int retval;
char str[MAX];
struct sockaddr_un addr;
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
printk(KERN_INFO "Start client module.\n");
// create
retval = sock_create(AF_UNIX, SOCK_STREAM, 0, &sock);
// connect
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCK_PATH);
retval = sock->ops->connect(sock, (struct sockaddr *)&addr, sizeof(addr), 0);
// recvmsg
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov->iov_base= str;
msg.msg_iov->iov_len= strlen(str)+1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
oldfs = get_fs();
set_fs(KERNEL_DS);
retval = sock_recvmsg(sock, &msg, strlen(str)+1, 0);
set_fs(oldfs);
// print str
printk(KERN_INFO "client module: %s.\n",str);
// release socket
sock_release(sock);
return 0;
}
static void __exit client_module_exit( void )
{
printk(KERN_INFO "Exit client module.\n");
}
module_init( client_module_init );
module_exit( client_module_exit );
MODULE_LICENSE("GPL");
用戶服務器
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCK_PATH "/tmp/usocket"
int send_msg_to_client(int socketfd, char* data) {
struct msghdr msg;
struct iovec iov;
int s;
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov.iov_base = data;
iov.iov_len = strlen(data)+1;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
s = sendmsg(socketfd, &msg, 0);
printf("after send - iov_base: %s, length: %d\n", (char *) msg.msg_iov->iov_base, (int) strlen(msg.msg_iov->iov_base));
if(s < 0){
perror("sendmsg");
return 0;
}
return s;
}
int main(int argc, char* argv[])
{
if (argc != 2) {
printf("Usage: $ %s <string>\n",argv[0]);
return 0;
}
int s, s2, len, slen;
socklen_t t;
struct sockaddr_un local, remote;
char* data = argv[1];
printf("print data: %s\n",data);
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
memset(&local, 0, sizeof(local));
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
perror("bind");
exit(1);
}
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
printf("Waiting for a connection...\n");
t = sizeof(remote);
if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
slen = send_msg_to_client(s2, data);
if(slen < 0)
perror("send");
close(s2);
return 0;
}
用戶客戶端
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCK_PATH "/tmp/usocket"
#define MAX 100
int recv_msg_from_server(int socketfd, char data[MAX]) {
struct msghdr msg;
struct iovec iov;
int s;
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov.iov_base = data;
iov.iov_len = MAX;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
s = recvmsg(socketfd, &msg, 0);
printf("after recv - iov_base: %s, length: %d\n", (char *) msg.msg_iov->iov_base, (int) strlen(msg.msg_iov->iov_base));
if(s < 0){
perror("recvmsg");
}
return s;
}
int main(void)
{
int s, len, slen;
struct sockaddr_un remote;
char data[MAX];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
printf("Trying to connect...\n");
memset(&remote, 0, sizeof(remote));
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SOCK_PATH);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected.\n");
slen = recv_msg_from_server(s, data);
if (slen < 0) {
perror("recvmsg");
}
//printf("print data received > %s\n", data);
close(s);
return 0;
}
我剛剛遇到了類似的問題(盡管使用 UNIX 數據報),我相信作為內核一部分的unix_mkname()函數中存在一個錯誤:該函數確保給定sockaddr_un
結構的sun_path
字段始終以空值終止通過在該字段的末尾添加一個 NUL 字符。 由於這是最后一個字段,它將破壞分配該結構的區域(堆棧或堆)。
最好的解決方案是在內核源代碼中修補該函數,使其僅將sun_path
字段的最后一個字符設置為 NUL。 同時,您可以通過將sockaddr_un
結構的大小減少 1 個字節來使代碼正常工作:
模塊服務器
// bind
retval = sock->ops->bind(sock,(struct sockaddr *)&addr, sizeof(addr) - 1);
模塊客戶端
retval = sock->ops->connect(sock, (struct sockaddr *)&addr, sizeof(addr) - 1, 0);
當我使用 UNIX 數據報時,我無法確定它是否修復了bind()
和connect()
,但至少,它修復了sock_sendmsg()
。
@shu-suzuki:感謝您的指導。
5 年后我回到這里......我再次陷入這個問題,發現我在 5 年前做了同樣的事情。
原因與 Tey 的答案相同,解決方案還可以(從長度減去 1),但這是另一種解決方案。
您可以做的是使用struct sockaddr_storage
作為struct sockaddr_un
。 就我讀過的內核代碼而言,這就是他們用來處理來自用戶空間的綁定系統調用的內容。 sockeaddr_storage
比sockaddr_un
因此內核代碼可以寫入超過 sizeof(sockaddr_un)。
代碼應該像
struct sockaddr_storage addrStorage;
struct sockaddr_un* addr;
memset(&addrStorage, 0, sizeof(addrStorage));
addr = (struct sockaddr_un*)&addrStorage;
addr->sun_family = AF_UNIX;
strcpy(addr->sun_path, "/my/sock/name");
kernel_bind(listenSock, (struct sockaddr*)addr, sizeof(*addr));
這看起來對我有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.