rmgoncalo
rmgoncalo

Reputation: 587

Linux Kernel Module unix domain sockets

I'm trying to share data between kernel 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 acting as socket client and a c program acting as socket server. And vice-versa, a kernel module acting as socket server and a c program acting as socket client.

Programs are very simple. Servers just send a string to clients and they print it.

I run server_user.c and then I try to insert 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;
}

Upvotes: 3

Views: 4858

Answers (2)

Shu Suzuki
Shu Suzuki

Reputation: 1184

After 5 years I came back here... I again stucked with this problem and found that I did the same thing 5 years ago.

The cause is as in Tey's answer and the solution is OK (sutbract 1 from the length) but here's another solution.

What you can do is to use struct sockaddr_storage as struct sockaddr_un. As far as I read the kernel code this is what they use to handle bind system call from user space. sockeaddr_storage is larger than sockaddr_un so kernel code can write beyond the sizeof(sockaddr_un).

The code should be like

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

This looks working for me.

Upvotes: 1

Tey&#39;
Tey&#39;

Reputation: 990

I have just faced a similar issue (with UNIX datagrams though), and I believe there is a bug in the unix_mkname() function which is part of the kernel: this function makes sure the sun_path field of the given sockaddr_un structure is always null terminated by adding a NUL character after the end of that field. Since this is the last field, it will corrupt the area (stack or heap) where that structure has been allocated.

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

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

As I am using UNIX datagrams, I cannot really tell if it fixes the issue for bind() and connect(), but a least, it fixes it for sock_sendmsg().

@shu-suzuki: thanks for the leads.

Upvotes: 3

Related Questions