Android – Linux kernel module unix domain socket

Linux kernel module unix domain socket… here is a solution to the problem.

Linux kernel module unix domain socket

I’m trying to share data between kernel space and user space. The ultimate goal is to port it to Android, so I’m using unix domain sockets. (I don’t know if this is the best option).

I have a kernel module that works as a socket client and a c program that acts as a socket server. Vice versa, a kernel module acts as a socket server and a C program acts as a socket client.

The procedure is very simple. The server simply sends a string to the client, which then prints it.

I ran server_user.c and then tried inserting client_module.ko using insmod. I get the following error:

Kernel panic - not syncing: stack - protector: Kernel stack is corrupted in: ffffffffa0005157

drm-kms-helper: panic occurred, switching back to text console 

What’s wrong?

Module server

#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");

Module client

#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");

User server

#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;
}

User client

#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;
}

Solution

I just had a similar issue (albeit with UNIX datagrams) and I believe unix_mkname(). There is an error in the function that is part of the kernel: This function ensures that the sun_path field of a given sockaddr_un structure is always passed after the end of the field. Since this is the last field, it destroys the area (stack or heap) where the structure is allocated.

The best solution is to patch the function in the kernel source so that it only sets the last character of the sun_path field to NUL. At the same time, you can make your code work by reducing the size provided for the sockaddr_un structure by 1 byte:

Module server

  // bind
  retval = sock->ops->bind(sock,(struct sockaddr *)&addr, sizeof(addr) - 1);

Module client

 retval = sock->ops->connect(sock, (struct sockaddr *)&addr, sizeof(addr) - 1, 0);

Because I’m using UNIX datagrams, I can’t be sure if it solves the bind() and connect() problem, but at least, it solves sock_sendmsg().

@shu-suzuki: Thanks for the clues.

Related Problems and Solutions