Showing posts with label android kernel. Show all posts
Showing posts with label android kernel. Show all posts

Monday, November 23, 2015

IOCTL Android

IOCTL is a very useful system call: it is simple and multiplexes the different commands to the appropriate kernel space function. In this post, I want to describe how you can implement a module with IOCTL support for Android. There are a lot of good articles about it (links below), and I just describe the differences regarding to the Android platform.

So, let's create a very simple misc device and let's play with it, doing some reads and writes. Initially, let's define a very simple kernel module (most of the code was taken from here).

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>


#define MY_MACIG 'G'
#define READ_IOCTL _IOR(MY_MACIG, 0, int)
#define WRITE_IOCTL _IOW(MY_MACIG, 1, int)
 
static int used; 
static char msg[200];

static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
  return simple_read_from_buffer(buffer, length, offset, msg, 200);
}

static ssize_t device_write(struct file *filp, const char __user *buff, size_t len, loff_t *off)
{
  if (len > 199)
    return -EINVAL;
  copy_from_user(msg, buff, len);
  msg[len] = '\0';
  return len;
}

static int device_open(struct inode *inode, struct file *file)
{
  used++;
  return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
  used--;
  return 0;
}

char buf[200];
int device_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
  int len = 200;
  switch(cmd) {
  case READ_IOCTL: 
    copy_to_user((char *)arg, buf, 200);
    break;
 
  case WRITE_IOCTL:
    copy_from_user(buf, (char *)arg, len);
    break;

  default:
    return -ENOTTY;
  }
  return len;
}

static struct file_operations fops = {
        .owner = THIS_MODULE,
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .release = device_release,
        .unlocked_ioctl = device_ioctl
};

static struct miscdevice my_miscdev = {
        .name         = "my_device",
        .mode         = S_IRWXUGO,
        .fops         = &fops,
};

static int __init cdevexample_module_init(void)
{
  int ret = misc_register(&my_miscdev);
  if (ret < 0) {
    printk ("Registering the character device failed\n");
    return ret;
  }
  printk("create node with mknod /dev/my_device\n");
  return 0;
}

static void __exit cdevexample_module_exit(void)
{
  misc_deregister(&my_miscdev);
}  

module_init(cdevexample_module_init);
module_exit(cdevexample_module_exit);
MODULE_LICENSE("GPL");

Compile it and insert into Android kernel.

shell@flounder:/ $ su
shell@flounder:/ $ insmod my_module.ko

Now, we can check the new device in /dev:

shell@flounder:/ $ su
shell@flounder:/ $ ls -l /dev
...
crw-rw---- root     mtp       10,  23 2015-11-23 18:04 mtp_usb
crw------- root     root      10,   0 2015-11-23 18:11 my_device
crw------- root     root      10,  36 2015-11-23 18:04 network_latency
crw------- root     root      10,  35 2015-11-23 18:04 network_throughput
crw-rw-rw- root     root       1,   3 2015-11-23 18:04 null

...

See that the permissions are limited. Don't forget to set it to:

shell@flounder:/ $ chmod 666 /dev/my_device
shell@flounder:/ $ ls -l /dev
...
crw-rw---- root     mtp       10,  23 2015-11-23 18:04 mtp_usb
crw-rw-rw- root     root      10,   0 2015-11-23 18:11 my_device
crw------- root     root      10,  36 2015-11-23 18:04 network_latency
crw------- root     root      10,  35 2015-11-23 18:04 network_throughput
crw-rw-rw- root     root       1,   3 2015-11-23 18:04 null

...

Now, let's try to do some operations with our device driver:

shell@flounder:/ $ echo "Hello world" > /dev/my_device
shell@flounder:/ $ cat /dev/my_device

You will see the following error on the logcat:

avc: denied { read write } for name="my_device" dev="tmpfs" scontext=u:r:system_app:s0 tcontext

This means that SELinux (yes, Android makes heavy usage of it) also controls the access to device drivers and you cannot read/write from/to your new drive. You have two options: i) disable SELinux in Android (you need to change some kernel options and rebuild it) or ii) add some new rules into SELinux. Let's do the last to learn a bit more :-)

So, we change the following files and give access (read, write, getattr, ioctl, open and create) to our new device /dev/my_device. If you need to restrict the access, you can adapt the policies according to your needs. For more information about SELinux and Android, take a look in this doc (specially the section "Implementation").

external/sepolicy/device.te
type fscklogs, dev_type;
type full_device, dev_type;
type my_device, dev_type;


external/sepolicy/file_contexts
/dev/rproc_user        u:object_r:rpmsg_device:s0
/dev/my_device         u:object_r:my_device:s0
/dev/snd(/.*)?         u:object_r:audio_device:s0


external/sepolicy/app.te
allow appdomain usb_device:chr_file { read write getattr ioctl };
allow appdomain usbaccessory_device:chr_file { read write getattr };
allow appdomain my_device:chr_file { read write getattr ioctl open create };


Now, let's build the Android framework again and flash the device. Everything should work fine.

shell@flounder:/ $ echo "Hello world" > /dev/my_device
shell@flounder:/ $ cat /dev/my_device
Hello world

That's it!! You can also check the following links

  • http://www.all-things-android.com/content/understanding-se-android-policy-files
  • https://source.android.com/security/selinux/

Monday, September 7, 2015

Asynchronous and synchronous I/O in Android NDK

This week, I had some issues on my tests in a Lollipop device with AOSP. I really wanted to have synchronous reads/writes so that I could evaluate my solution in a different way. I am also trying to minimize the effects of caching as much as possible. If you check for async and sync I/O operations for Linux, you'll a lot of references to the flags O_DIRECT, O_SYNC, O_DSYNC, ...

Actually, that's a good way to implement. However, with Android NDK apps, things are not so straightforward.

First, to have a good understanding about the O_*SYNC flags, check this link. Of course, the man page for the command open as well.

First, O_DIRECT does not work in Android since 4.4. See this link for more details about it.  That's sad. So, let's try to use the O_*SYNC data.

O_SYNC and O_DSYNC work fine in Android. But, as the description say, only for writes. Another detail: for Android, O_SYNC has the same semantics as O_DSYNC. That's good, but I still want something similar to reads as well.

Why don't we use O_RSYNC? Well, Android does not implement it :-( But it's not the only one... there are other Linux distributions that don't do it either.

What about dropping caches?? See this link for more details. Hum, that works, but after the first read, the data will be cached again :-(

So, I am still looking for a solution for Android Lollipop. Hope to post it soon!


Sunday, June 14, 2015

UEvents in Android - from kernel events to notifications

In my current project, the system has to notify the user about a certain action that actually takes place inside the kernel (in this case, transfer of security keys). I don't want to get into too much details about the tasks, but let's consider that the kernel and the Dalvik are the only trustworthy components and the security keys are stored inside the kernel. I've also seen some pieces of code that helps to implement this, but I could not find a end-to-end solution.

[This post is a bit long, as it contains a lot of code and I tried to explain some details of it.]

Ok, let's proceed. This is the flow:

1) The kernel is asked to perform a certain syscall (transfer securely stored keys inside the kernel);
2) The kernel has to notify the user that some application has asked to do something suspicious, for example, to have access to some security keys;
3) The event is sent all the way to the userspace and a notification appears to the user (UI).

First, we have to generate one event in the kernel space (you can create a module and insert your code there). For this, we use an UEVENT. In addition, we can also add extra pieces of information into the event, for example strings, integers and so forth. The following code shows a simple UEVENT generated from the kernel space. It contains one integer field called "etype".

Some pieces of code were taken from the kernel itself (linux/samples/kobject/kset-example.c), and I made small changes to keep it even simpler. For more information, check the original one.

Initially, we need to create a kobject to embedded an attribute, for instance (https://www.kernel.org/doc/Documentation/kobject.txt). Once the kobject is registered, we broadcast it to other components of the system using kobject_uevent(). The files foo_kobject.c/h and foo_event.c/h contain code to broadcast an UEvent using a kobject.

foo_kobject.h (kernel code, for example, as part of your module)
#ifndef FOO_KOBJECT_
#define FOO_KOBJECT_

/* 
 * Partially copied from linux/samples/kobject/kset-example.c
 *
 * Released under the GPL version 2 only.
 */

/*
 * This is our "object" that we will create and register it with sysfs.
 */
struct foo_obj {
 struct kobject kobj;
 int foo;
};
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)

struct foo_obj *
create_foo_obj(const char *name);

int 
foo_kobj_init(void);

void
foo_kobj_exit(void);

#endif

foo_kobject.c (kernel code, for example, as part of your module)
/*
 * Partially copied from linux/samples/kobject/kset-example.c
 *
 * Released under the GPL version 2 only.
 *
 */

#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>

#include "foo_kobject.h"

/*
 * This module shows how to create a kset in sysfs called
 * /sys/kernel/foo
 * Then one kobject is created and assigned to this kset, "foo".  
 * In this kobject, one attribute is also
 * created and if an integer is written to these files, it can be later
 * read out of it.
 */

/* a custom attribute that works just for a struct foo_obj. */
struct foo_attribute {
 struct attribute attr;
 ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
 ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
};
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)

/*
 * The default show function that must be passed to sysfs.  This will be
 * called by sysfs for whenever a show function is called by the user on a
 * sysfs file associated with the kobjects we have registered.  We need to
 * transpose back from a "default" kobject to our custom struct foo_obj and
 * then call the show function for that specific object.
 */
static ssize_t foo_attr_show(struct kobject *kobj,
        struct attribute *attr,
        char *buf)
{
 struct foo_attribute *attribute;
 struct foo_obj *foo;

 attribute = to_foo_attr(attr);
 foo = to_foo_obj(kobj);

 if (!attribute->show)
  return -EIO;

 return attribute->show(foo, attribute, buf);
}

/*
 * Just like the default show function above, but this one is for when the
 * sysfs "store" is requested (when a value is written to a file.)
 */
static ssize_t foo_attr_store(struct kobject *kobj,
         struct attribute *attr,
         const char *buf, size_t len)
{
 struct foo_attribute *attribute;
 struct foo_obj *foo;

 attribute = to_foo_attr(attr);
 foo = to_foo_obj(kobj);

 if (!attribute->store)
  return -EIO;

 return attribute->store(foo, attribute, buf, len);
}

/* Our custom sysfs_ops that we will associate with our ktype later on */
static const struct sysfs_ops foo_sysfs_ops = {
 .show = foo_attr_show,
 .store = foo_attr_store,
};

/*
 * The release function for our object.  This is REQUIRED by the kernel to
 * have.  We free the memory held in our object here.
 *
 * NEVER try to get away with just a "blank" release function to try to be
 * smarter than the kernel.  Turns out, no one ever is...
 */
static void foo_release(struct kobject *kobj)
{
 struct foo_obj *foo;

 foo = to_foo_obj(kobj);
 kfree(foo);
}

/*
 * The "foo" file where the .foo variable is read from and written to.
 */
static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
   char *buf)
{
 return sprintf(buf, "%d\n", foo_obj->foo);
}

static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
    const char *buf, size_t count)
{
 sscanf(buf, "%du", &foo_obj->foo);
 return count;
}

static struct foo_attribute foo_attribute =
 __ATTR(foo, 0666, foo_show, foo_store);

/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *foo_default_attrs[] = {
 &foo_attribute.attr,
 NULL, /* need to NULL terminate the list of attributes */
};

/*
 * Our own ktype for our kobjects.  Here we specify our sysfs ops, the
 * release function, and the set of default attributes we want created
 * whenever a kobject of this type is registered with the kernel.
 */
static struct kobj_type foo_ktype = {
 .sysfs_ops = &foo_sysfs_ops,
 .release = foo_release,
 .default_attrs = foo_default_attrs,
};

static struct kset *example_kset;

struct foo_obj *create_foo_obj(const char *name)
{
 struct foo_obj *foo;
 int retval;

 /* allocate the memory for the whole object */
 foo = kzalloc(sizeof(*foo), GFP_KERNEL);
 if (!foo)
  return NULL;

 /*
  * As we have a kset for this kobject, we need to set it before calling
  * the kobject core.
         */
 foo->kobj.kset = example_kset;

 /*
  * Initialize and add the kobject to the kernel.  All the default files
  * will be created here.  As we have already specified a kset for this
  * kobject, we don't have to set a parent for the kobject, the kobject
  * will be placed beneath that kset automatically.
  */
 retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
 if (retval) {
  kobject_put(&foo->kobj);
  return NULL;
 }

 /*
  * We are always responsible for sending the uevent that the kobject
  * was added to the system.
  */
 kobject_uevent(&foo->kobj, KOBJ_ADD);

 return foo;
}

static void destroy_foo_obj(struct foo_obj *foo)
{
 kobject_put(&foo->kobj);
}

int
foo_kobject_init(void)
{
 /*
  * Create a kset with the name of "kset_foo",
  * located under /sys/kernel/
  */
 example_kset = kset_create_and_add("kset_foo", NULL, kernel_kobj);
 if (!example_kset)
  return -ENOMEM;

}

void 
foo_kobject_exit(void)
{
 destroy_foo_obj(foo_kobj);
 kset_unregister(example_kset);
}

foo_uevent.h (kernel code, for example, as part of your module)
#ifndef FOO_UEVENT_
#define FOO_UEVENT_

enum FOO_event_type {
  FOO_GET = 1,
  FOO_SET
};

struct foo_event {
  enum foo_event_type etype;
};

int foo_init_events(void);

int foo_send_uevent(struct foo_event *fooe);

#endif


In the following code, we send an event string as part of our new UEvent. In our case, just the type of the event (FOO_GET or FOO_SET). foo_uevent.c (kernel code, for example, as part of your module)
#include <linux/kobject.h>
#include "foo_kobject.h"
#include "foo_uevent.h"

static struct foo_obj *foo_kobj;

int foo_init_events(void)
{
  int ret;

  ret = example_init();
  if (ret) 
  { 
    printk("error - could not create ksets\n");  
    goto foo_error;
  } 
 
  foo_kobj = create_foo_obj("foo");
  if (!foo_kobj)
  {
    printk("error - could not create kobj\n"); 
    goto foo_error;
  }
 
  return 0;

foo_error:
  return -EINVAL;
}

int foo_send_uevent(struct foo_event *pce)
{
  char event_string[20];
  char *envp[] = { event_string, NULL };

  if (!foo_kobj)
    return -EINVAL;

  snprintf(event_string, 20, "FOO_EVENT=%d", pce->etype);

  return kobject_uevent_env(&foo_kobj->kobj, KOBJ_CHANGE, envp);
}

So far, we've only made changes in the kernel level. But also, we need to listen to UEVENTs in the userspace. For that, I changed a little bit the Dalvik and added the following binder to listen to UEvents and send notifications, so that the user can see the event.

For more details on how to add a system service in Android, check this link. In this case, I added a Binder, as we are not providing any service (setting an integer, for instance). In our example, we simply broadcast events to the user interface.

To add the following Binder, create a file that contains the code into framework/base/services/java/com/android/server/. You also have to change the file Android.mk at framework/base/services/jni/Android.mk

...
LOCAL_SRC_FILES:= \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
     com_android_server_connectivity_Vpn.cpp \
+    com_android_server_pm_PackageManagerService.cpp \
     onload.cpp
...

Also, don't forget to register the service (check the link that I mentioned before!), by changing the file framework/base/services/java/com/android/server/SystemServer.java

class ServerThread extends Thread {
...
         WifiP2pService wifiP2p = null;
         WifiService wifi = null;
         NsdService serviceDiscovery= null;
+        MyServiceBinder myService = null;
         IPackageManager pm = null;
         Context context = null;
         WindowManagerService wm = null;
...
         class ServerThread extends Thread {
...
             }

             try {
+                Slog.i(TAG, "My Service - Example");
+                myService = MyServiceBinder.create(context);
+                ServiceManager.addService(
+                        Context.MY_SERVICE, myService);
+            } catch (Throwable e) {
+                reportWtf("starting Privacy Capsules Service", e);
+            }
+
...

Finally, here is the Java code. This Binder simply listens to UEvents that contains the string "FOO_EVENT", which also embeds the integer as part of the UEvent. Then, we create a UI notification and we attach an Intent to the notification, so that when the user clicks on it, a Activity or a Dialog is launched.
...
public class MyServiceBinder extends Binder {

    private final int FOO_GET = 1;
    private final int FOO_SET = 2;

    private MyServiceWorkerThread mWorker;
    private MyServiceWorkerHandler mHandler;
    private Context mContext;
    private NotificationManager mNotificationManager;

    /*
     * We create an observer to listen to the UEvent we are interested in. 
     * See UEventObserver#startObserving(String). From the UEvent, we extract 
     * an existing integer field and we propagate create a notification to 
     * be seen by the user.
     */
    private final UEventObserver mUEventObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            final int eventType = Integer.parseInt(event.get("FOO_EVENT"));
            sendMyServiceNotification(eventType);
        }
    };

    public MyServiceBinder(Context context) {
        super();
        mContext = context;
        mNotificationManager = (NotificationManager)mContext.getSystemService(
                               Context.NOTIFICATION_SERVICE);

         /* We have to specify a filter to receive only certain UEvents from the 
          * kernel. In this case, the UEVENT that we want to listen to contains 
          * the string "FOO_EVENT".
          */
        mUEventObserver.startObserving("FOO_EVENT=");

        mWorker = new MyServiceWorkerThread("MyServiceWorker");
        mWorker.start();
    }

    /*
     * Add potential initialization steps here
     */
    public static MyServiceBinder create(Context context) {
        MyServiceBinder service = new MyServiceBinder(context);
        return service;
    }
    
    /*
     * Sends notification to the user. In this case, we are create notifications. 
     * If the user clicks on the notification, we send an Intent so that an system 
     * application is launched. For example, if we are listening to notifications 
     * for USB UEvents, the USB Settings application can be launched when the 
     * notification is clicked.
     */
    private final void sendNotificationToUser(int type) {
        String notificationMessage = "";
        Resources r = mContext.getResources();
        switch(type) {
                case FOO_GET:
                // do something if the event type is A
                notificationMessage = "This is event type FOO_GET";
                break;
                case FOO_SET:
                // do something if the event type is B
                notificationMessage = "This is event type FOO_SET";      
                break;
                default:
                break;
        }  

        String notificationTitle = r.getString(R.string.my_service_notification_title);
        long notificationWhen = System.currentTimeMillis();
        int requestID = (int) notificationWhen;
 
        Intent intent = new Intent(Intent.ACTION_FOO_EVENT);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        intent.putExtra(FooManager.EXTRA_ETYPE, type);
        intent.putExtra(FooManager.EXTRA_MESSAGE, notificationMessage);

        PendingIntent pi = PendingIntent.getActivityAsUser(mContext, requestID,
                                intent, 0, null, UserHandle.CURRENT);

        Notification notification = new Notification.Builder(mContext)
                        .setSmallIcon(R.drawable.indicator_input_error)
                        .setContentTitle(notificationTitle)
                        .setContentText(notificationMessage)
                        .setPriority(Notification.PRIORITY_HIGH)
                        .setDefaults(0)
                        .setWhen(notificationWhen)
                        .setOngoing(true)
                        .setContentIntent(pi)
                        .setAutoCancel(true)
                        .build();

        mNotificationManager.notifyAsUser(null,
                        R.string.my_service_notification_title,
                        notification, UserHandle.ALL);
    }


    /*
     * Runner for the 
     *
     */
    private void sendMyServiceNotification(final int type) {
        
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                sendNotificationToUser(type);
            }
        });

    }

    private class MyServiceWorkerThread extends Thread {

        public MyServiceWorkerThread(String name) {
            super(name);
        }

        public void run() {
            Looper.prepare();
            mHandler = new MyServiceWorkerHandler();
            Looper.loop();
        }

    }
 
    private class MyServiceWorkerHandler extends Handler {

        private static final int MESSAGE_SET = 0;

        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
                    Log.i(TAG, "set message received: " + msg.arg1);
                }
            } catch (Exception e) {
                // Log, don't crash!
                Log.e(TAG, "Exception in MyServiceWorkerHandler.handleMessage:", e);
            }
        }

    }
}