0

I writing a kernel module which accepts data(form of a structure) and passes back some data(same structure format) to the user-space. I can successfully receive the message from user but i get segmentation fault while i try tp dereference some of the members of the structure of the data received from kernel. I am using netlink socket API.

Sample code is as below:

user-space.c

#define NETLINK_USER 27

#define MAX_PAYLOAD 10000 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
Response *p;
Response *req;

test r1;
test r2;

int main()
{

char *data;
data = malloc(4096 * sizeof(char));;
data = "data from user";
sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if(sock_fd<0)
return -1;

memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); 

bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));

memset(&dest_addr, 0, sizeof(dest_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; 
dest_addr.nl_groups = 0; 

nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len = NLMSG_LENGTH(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;

p = malloc(2*sizeof(Response));

p[0].index = 1;

p[0].dataSize = 2;

p[0].data = data;
p[0].test2 = 3;
p[0].test3 = 4;
p[0].test4 = 5;

r1.t = 10;
r1.ip_addr = malloc(50*sizeof(char));
r1.ip_addr = "192.168.10.2";

p[0].test = &r1;

/* Extra for testing */
p[1].index = 2;
p[1].dataSize = 3;
//strcpy(p[1].data , "Data2 from User");
p[1].data = data;
p[1].test2 = 4;
p[1].test3 = 5;
p[1].test4 = 6;

r2.t = 20;
r2.ip_addr = malloc(50*sizeof(char));
r2.ip_addr = "192.168.20.2";

p[1].test = &r2;
memcpy(NLMSG_DATA(nlh), (void *)p, 2 * sizeof(*p));

iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

printf("Sending message to kernel\n");
sendmsg(sock_fd,&msg,0);
printf("Waiting for message from kernel\n");

/* Read message from kernel */
//recvmsg(sock_fd, &msg, 0);
req = (Response*)NLMSG_DATA(nlh);

printf("Recieved from Kernel:\n"); 
printf("index %d\n", req[0].index);
printf("dataSize %d\n", req[0].dataSize);
printf("data: %s\n", req[0].data);    <---Segmentation fault from here
printf("test2 %d\n", req[0].test2);
printf("test3 %d\n", req[0].test3);
printf("test4 %d\n", req[0].test4);


printf("contents of test structure are %d,%s\n",req[0].test->t,req[0].test->ip_addr);  <-----Segmentation fault 

close(sock_fd);

Kernel_module.c

#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/slab.h>


#define NETLINK_USER 27

struct sock *nl_sk = NULL;
typedef struct _Response Response;

typedef struct _test test;


struct _test{

    int t;
    char *ip_addr;
};

struct _Response
{
  int index;
  int dataSize;     

char *data;
  int test2;
  int test3;
  int test4;

  test *test;
};



static void hello_nl_recv_msg(struct sk_buff *skb) {

struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
int res;
Response *req;
Response *req1;
test t;

t.t = 1;
t.ip_addr = kmalloc(50*sizeof(char), GFP_KERNEL);
t.ip_addr = "129.63.45.1";
req1 = (Response *)kmalloc_array(2, sizeof(Response), GFP_KERNEL);

char *data;
data = kmalloc(4096 * sizeof(char), GFP_KERNEL);
data = "data from kernel";
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);

msg_size= 2 * sizeof(*req1);


req1[0].index = 100;
req1[0].dataSize = 100;

req1[0].data = data;
req1[0].test2 = 100;
req1[0].test3 = 100;
req1[0].test4 = 100;
req1[0].test = &t;
printk("Sending to Userspace:\n"); 


/*Second set of message*/
req1[1].index = 102;
req1[1].dataSize = 103;
//strcpy(req1[1].data , "Data from Kernel");
req1[1].data = data;
req1[1].test2 = 100;
req1[1].test3 = 100;
req1[1].test4 = 100;
req1[1].test = &t;

nlh=(struct nlmsghdr*)skb->data;

req = (Response *)NLMSG_DATA(nlh);   <--message received from user-space


printk("Recieved from Userspace:\n"); 
printk("index %d\n", req[0].index);
printk("dataSize %d\n", req[0].dataSize);
printk("data: %s\n", req[0].data);
printk("test2 %d\n", req[0].test2);
printk("test3 %d\n", req[0].test3);
printk("test4 %d\n", req[0].test4);
printk("contents of test are %d, %s\n",req[0].test->t,req[0].test->ip_addr);


printk("Next set of data\n");
printk("Recieved from Userspace:\n"); 
printk("index %d\n", req[1].index);
printk("dataSize %d\n", req[1].dataSize);
printk("data: %s\n", req[1].data);
printk("test2 %d\n", req[1].test2);
printk("test3 %d\n", req[1].test3);
printk("test4 %d\n", req[1].test4);
printk("contents of test are %d, %s\n",req[1].test->t,req[1].test->ip_addr);

pid = nlh->nlmsg_pid; /*pid of sending process */

skb_out = nlmsg_new(10000,0);

if(!skb_out)
{

    printk(KERN_ERR "Failed to allocate new skb\n");
    return;

} 
nlh=nlmsg_put(skb_out,0,0,NLMSG_DONE,10000,0);  
NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */



/*End of second set*/

memcpy(NLMSG_DATA(nlh), req1, 2 * sizeof(*req1));

res=nlmsg_unicast(nl_sk,skb_out,pid);

if(res<0)
    printk(KERN_INFO "Error while sending bak to user\n");
}

static int __init hello_init(void) {

printk("Entering: %s\n",__FUNCTION__);

struct netlink_kernel_cfg cfg = {
    .input = hello_nl_recv_msg,
};

nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);

if(!nl_sk)
{

    printk(KERN_ALERT "Error creating socket.\n");
    return -10;

}

return 0;
}

static void __exit hello_exit(void) {

printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}

module_init(hello_init); module_exit(hello_exit);

MODULE_LICENSE("GPL");

}

global.h

#ifndef __GLOBAL_H
#define __GLOBAL_H

typedef struct _Response Response;
typedef struct _test test;


struct _test{

        int t;
        char *ip_addr;
}; 

struct _Response
{
  int index;
  int dataSize;     

  char *data;
  int test2;
  int test3;
  int test4;

  test *test;
};

#endif

Basically the problem is with the pointers. If i use char data[4096] instead of character pointer, i receive the message. But i get segmentation fault while dereferencing the test structure. How to resolve this issue?

Thanks

lone_wolf
  • 1
  • 1
  • 3

1 Answers1

0

This code is very wrong and full critique is likely unnecessary, so I'll just point out 2 most obvious types of violations.

char *data;
data = kmalloc(4096 * sizeof(char), GFP_KERNEL);
data = "data from kernel";

First a nit: sizeof(char) is guaranteed to be 1. The real problem though is that this instantly overwrites the value of data, losing the pointer returned by malloc. This is present in multiple places in the code.

req = (Response *)NLMSG_DATA(nlh);   <--message received from user-space
printk("Recieved from Userspace:\n"); 
printk("index %d\n", req[0].index);
printk("dataSize %d\n", req[0].dataSize);
printk("data: %s\n", req[0].data);

This code, while likely "works", is completely wrong. The userspace buffer (.data) cannot be safely accessed in this manner and on some architectures it cannot be accessed at all. It may "work" because the complete address space is divided into both the kernel and userspace and the kernel in principle has access there. Problems arise if the pointer is to something totally bogus or within the kernel, or maybe to something unmapped.

The userspace portion of the code tries to do a reverse trick and unsurprisingly that fails -- you can't access kernel memory or there would be no security whatsoever.

The problem only arises because the protocol is wrong. You should have a buffer allocated in userspace and a variable holding its size. Then you can tell the kernel where to put data (and how much tops).

Either way, I think you are not ready for kernel work at this point and as such strongly suggest sticking to userspace for the time being.