xorl %eax, %eax

CVE-2010-1187: Linux kernel TIPC NULL Pointer Dereference

leave a comment »

This vulnerability was reported by Neil Horman and it affects Linux kernel 2.6.33 and probably other releases too. The problem appears in Transparent Inter-Process Communication (TIPC) code and specifically in the code below as seen at net/tipc/core.c file.

static int __init tipc_init(void)
{
      ...
        if ((res = tipc_core_start()))
                err("Unable to start in single node mode\n");
      ...
        return res;
}

This is the module’s initialization routine and as you can read it will invoke tipc_core_start() in order to start the module. This among others will execute the following…

/* global variables used by multiple sub-systems within TIPC */

int tipc_mode = TIPC_NOT_RUNNING;
      ...
/**
 * tipc_core_start - switch TIPC from NOT RUNNING to SINGLE NODE mode
 */

int tipc_core_start(void)
{
      ...
        get_random_bytes(&tipc_random, sizeof(tipc_random));
        tipc_mode = TIPC_NODE_MODE;
      ...
        return res;
}

So, after initializing ‘tipc_random’ with random bytes it will set ‘tipc_mode’ to ‘TIPC_NODE_MODE’ which means that communication is allowed only for its own address. This is done because the ‘tipc_net’ structure isn’t initialized yet and as we can find at net/tipc/net.c it’s set to NULL like this:

struct network tipc_net = { NULL };

So even though the user should not be able to send any messages to other address there is nothing to stop him from doing so. A user can simply create an ‘AF_TIPC’ socket and send a datagram before the kernel module for TIPC enters its network mode. If this happens, then the code shown below will be executed:

static int net_init(void)
{
        memset(&tipc_net, 0, sizeof(tipc_net));
        tipc_net.zones = kcalloc(tipc_max_zones + 1, sizeof(struct _zone *), GFP_ATOMIC);
        if (!tipc_net.zones) {
                return -ENOMEM;
        }
        return 0;
}

But since the ‘tpic_net’ pointer is still NULL this will lead to accessing ‘(NULL).zones’ offset which in turn results in a kernel OOPS because of the NULL pointer dereference. To fix this the global ‘tipc_net’ pointer was changed like this:

 DEFINE_RWLOCK(tipc_net_lock);
-struct network tipc_net = { NULL };
+struct _zone *tipc_zones[256] = { NULL, };
+struct network tipc_net = { tipc_zones };

This will make the previous pointer pointing to an array with the specified number of elements which are all of them initialized to 0/NULL so that ‘tipc_net’ is not pointing to NULL anymore. In addition to this, by doing this there is no need for initialization routines since the space is already allocated in the new static array. For this reason the net_init() was removed:

-static int net_init(void)
-{
-       memset(&tipc_net, 0, sizeof(tipc_net));
-       tipc_net.zones = kcalloc(tipc_max_zones + 1, sizeof(struct _zone *), GFP_ATOMIC);
-       if (!tipc_net.zones) {
-               return -ENOMEM;
-       }
-       return 0;
-}
-

And the equivalent net_stop() was changed to remove the NULL pointer check along with the kfree() calls like this:

 static void net_stop(void)
 {
        u32 z_num;
 
-       if (!tipc_net.zones)
-               return;
-
-       for (z_num = 1; z_num <= tipc_max_zones; z_num++) {
+       for (z_num = 1; z_num <= tipc_max_zones; z_num++)
                tipc_zone_delete(tipc_net.zones[z_num]);
-       }
-       kfree(tipc_net.zones);
-       tipc_net.zones = NULL;
 }

Finally, tipc_net_start() was also updated since there is no net_init() function in the module…

 
-       if ((res = tipc_bearer_init()) ||
-           (res = net_init()) ||
-           (res = tipc_cltr_init()) ||
+       if ((res = tipc_cltr_init()) ||
            (res = tipc_bclink_init())) {

Written by xorl

April 5, 2010 at 23:20

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s