android: logger: Allow a UID to read it's own log entries
Nick Kralevich [Thu, 23 Feb 2012 18:09:06 +0000 (10:09 -0800)]
Modify the kernel logger to record the UID associated with
the log entries. Always allow the same UID which generated a
log message to read the log message.

Allow anyone in the logs group, or anyone with CAP_SYSLOG, to
read all log entries.

In addition, allow the client to upgrade log formats, so they
can get additional information from the kernel.

(cherry picked from android common tree commit
 d993be54c164ea473816f04745ae4f0504dbccfb)

NV Bug 1019928

Change-Id: Ie48fb614b43c9302a07ad2673b78dd8749b492b6
Signed-off-by: Nick Kralevich <nnk@google.com>
Signed-off-by: Dan Willemsen <dwillemsen@nvidia.com>
Reviewed-on: http://git-master/r/117175
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>

drivers/staging/android/logger.c
drivers/staging/android/logger.h

index fa76ce7..3e09d57 100644 (file)
@@ -57,6 +57,8 @@ struct logger_reader {
        struct logger_log       *log;   /* associated log */
        struct list_head        list;   /* entry in logger_log's list */
        size_t                  r_off;  /* current read head offset */
+       bool                    r_all;  /* reader can read all entries */
+       int                     r_ver;  /* reader ABI version */
 };
 
 /* logger_offset - returns index 'n' into the log via (optimized) modulus */
@@ -86,25 +88,71 @@ static inline struct logger_log *file_get_log(struct file *file)
 }
 
 /*
- * get_entry_len - Grabs the length of the payload of the next entry starting
- * from 'off'.
+ * get_entry_header - returns a pointer to the logger_entry header within
+ * 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must
+ * be provided. Typically the return value will be a pointer within
+ * 'logger->buf'.  However, a pointer to 'scratch' may be returned if
+ * the log entry spans the end and beginning of the circular buffer.
+ */
+static struct logger_entry *get_entry_header(struct logger_log *log,
+               size_t off, struct logger_entry *scratch)
+{
+       size_t len = min(sizeof(struct logger_entry), log->size - off);
+       if (len != sizeof(struct logger_entry)) {
+               memcpy(((void *) scratch), log->buffer + off, len);
+               memcpy(((void *) scratch) + len, log->buffer,
+                       sizeof(struct logger_entry) - len);
+               return scratch;
+       }
+
+       return (struct logger_entry *) (log->buffer + off);
+}
+
+/*
+ * get_entry_msg_len - Grabs the length of the message of the entry
+ * starting from from 'off'.
  *
  * Caller needs to hold log->mutex.
  */
-static __u32 get_entry_len(struct logger_log *log, size_t off)
+static __u32 get_entry_msg_len(struct logger_log *log, size_t off)
 {
-       __u16 val;
+       struct logger_entry scratch;
+       struct logger_entry *entry;
 
-       switch (log->size - off) {
-       case 1:
-               memcpy(&val, log->buffer + off, 1);
-               memcpy(((char *) &val) + 1, log->buffer, 1);
-               break;
-       default:
-               memcpy(&val, log->buffer + off, 2);
+       entry = get_entry_header(log, off, &scratch);
+       return entry->len;
+}
+
+static size_t get_user_hdr_len(int ver)
+{
+       if (ver < 2)
+               return sizeof(struct user_logger_entry_compat);
+       else
+               return sizeof(struct logger_entry);
+}
+
+static ssize_t copy_header_to_user(int ver, struct logger_entry *entry,
+                                        char __user *buf)
+{
+       void *hdr;
+       size_t hdr_len;
+       struct user_logger_entry_compat v1;
+
+       if (ver < 2) {
+               v1.len      = entry->len;
+               v1.__pad    = 0;
+               v1.pid      = entry->pid;
+               v1.tid      = entry->tid;
+               v1.sec      = entry->sec;
+               v1.nsec     = entry->nsec;
+               hdr         = &v1;
+               hdr_len     = sizeof(struct user_logger_entry_compat);
+       } else {
+               hdr         = entry;
+               hdr_len     = sizeof(struct logger_entry);
        }
 
-       return sizeof(struct logger_entry) + val;
+       return copy_to_user(buf, hdr, hdr_len);
 }
 
 /*
@@ -118,15 +166,30 @@ static ssize_t do_read_log_to_user(struct logger_log *log,
                                   char __user *buf,
                                   size_t count)
 {
+       struct logger_entry scratch;
+       struct logger_entry *entry;
        size_t len;
+       size_t msg_start;
 
        /*
-        * We read from the log in two disjoint operations. First, we read from
-        * the current read head offset up to 'count' bytes or to the end of
+        * First, copy the header to userspace, using the version of
+        * the header requested
+        */
+       entry = get_entry_header(log, reader->r_off, &scratch);
+       if (copy_header_to_user(reader->r_ver, entry, buf))
+               return -EFAULT;
+
+       count -= get_user_hdr_len(reader->r_ver);
+       buf += get_user_hdr_len(reader->r_ver);
+       msg_start = logger_offset(reader->r_off + sizeof(struct logger_entry));
+
+       /*
+        * We read from the msg in two disjoint operations. First, we read from
+        * the current msg head offset up to 'count' bytes or to the end of
         * the log, whichever comes first.
         */
-       len = min(count, log->size - reader->r_off);
-       if (copy_to_user(buf, log->buffer + reader->r_off, len))
+       len = min(count, log->size - msg_start);
+       if (copy_to_user(buf, log->buffer + msg_start, len))
                return -EFAULT;
 
        /*
@@ -137,9 +200,34 @@ static ssize_t do_read_log_to_user(struct logger_log *log,
                if (copy_to_user(buf + len, log->buffer, count - len))
                        return -EFAULT;
 
-       reader->r_off = logger_offset(reader->r_off + count);
+       reader->r_off = logger_offset(reader->r_off +
+               sizeof(struct logger_entry) + count);
 
-       return count;
+       return count + get_user_hdr_len(reader->r_ver);
+}
+
+/*
+ * get_next_entry_by_uid - Starting at 'off', returns an offset into
+ * 'log->buffer' which contains the first entry readable by 'euid'
+ */
+static size_t get_next_entry_by_uid(struct logger_log *log,
+               size_t off, uid_t euid)
+{
+       while (off != log->w_off) {
+               struct logger_entry *entry;
+               struct logger_entry scratch;
+               size_t next_len;
+
+               entry = get_entry_header(log, off, &scratch);
+
+               if (entry->euid == euid)
+                       return off;
+
+               next_len = sizeof(struct logger_entry) + entry->len;
+               off = logger_offset(off + next_len);
+       }
+
+       return off;
 }
 
 /*
@@ -151,7 +239,7 @@ static ssize_t do_read_log_to_user(struct logger_log *log,
  *     - If there are no log entries to read, blocks until log is written to
  *     - Atomically reads exactly one log entry
  *
- * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read
+ * Will set errno to EINVAL if read
  * buffer is insufficient to hold next entry.
  */
 static ssize_t logger_read(struct file *file, char __user *buf,
@@ -191,6 +279,10 @@ start:
 
        mutex_lock(&log->mutex);
 
+       if (!reader->r_all)
+               reader->r_off = get_next_entry_by_uid(log,
+                       reader->r_off, current_euid());
+
        /* is there still something to read or did we race? */
        if (unlikely(log->w_off == reader->r_off)) {
                mutex_unlock(&log->mutex);
@@ -198,7 +290,8 @@ start:
        }
 
        /* get the size of the next entry */
-       ret = get_entry_len(log, reader->r_off);
+       ret = get_user_hdr_len(reader->r_ver) +
+               get_entry_msg_len(log, reader->r_off);
        if (count < ret) {
                ret = -EINVAL;
                goto out;
@@ -224,7 +317,8 @@ static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)
        size_t count = 0;
 
        do {
-               size_t nr = get_entry_len(log, off);
+               size_t nr = sizeof(struct logger_entry) +
+                       get_entry_msg_len(log, off);
                off = logger_offset(off + nr);
                count += nr;
        } while (count < len);
@@ -336,7 +430,9 @@ ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
        header.tid = current->pid;
        header.sec = now.tv_sec;
        header.nsec = now.tv_nsec;
+       header.euid = current_euid();
        header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);
+       header.hdr_size = sizeof(struct logger_entry);
 
        /* null writes succeed, return zero */
        if (unlikely(!header.len))
@@ -409,6 +505,10 @@ static int logger_open(struct inode *inode, struct file *file)
                        return -ENOMEM;
 
                reader->log = log;
+               reader->r_ver = 1;
+               reader->r_all = in_egroup_p(inode->i_gid) ||
+                       capable(CAP_SYSLOG);
+
                INIT_LIST_HEAD(&reader->list);
 
                mutex_lock(&log->mutex);
@@ -463,6 +563,10 @@ static unsigned int logger_poll(struct file *file, poll_table *wait)
        poll_wait(file, &log->wq, wait);
 
        mutex_lock(&log->mutex);
+       if (!reader->r_all)
+               reader->r_off = get_next_entry_by_uid(log,
+                       reader->r_off, current_euid());
+
        if (log->w_off != reader->r_off)
                ret |= POLLIN | POLLRDNORM;
        mutex_unlock(&log->mutex);
@@ -470,11 +574,25 @@ static unsigned int logger_poll(struct file *file, poll_table *wait)
        return ret;
 }
 
+static long logger_set_version(struct logger_reader *reader, void __user *arg)
+{
+       int version;
+       if (copy_from_user(&version, arg, sizeof(int)))
+               return -EFAULT;
+
+       if ((version < 1) || (version > 2))
+               return -EINVAL;
+
+       reader->r_ver = version;
+       return 0;
+}
+
 static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct logger_log *log = file_get_log(file);
        struct logger_reader *reader;
-       long ret = -ENOTTY;
+       long ret = -EINVAL;
+       void __user *argp = (void __user *) arg;
 
        mutex_lock(&log->mutex);
 
@@ -499,8 +617,14 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        break;
                }
                reader = file->private_data;
+
+               if (!reader->r_all)
+                       reader->r_off = get_next_entry_by_uid(log,
+                               reader->r_off, current_euid());
+
                if (log->w_off != reader->r_off)
-                       ret = get_entry_len(log, reader->r_off);
+                       ret = get_user_hdr_len(reader->r_ver) +
+                               get_entry_msg_len(log, reader->r_off);
                else
                        ret = 0;
                break;
@@ -514,6 +638,22 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                log->head = log->w_off;
                ret = 0;
                break;
+       case LOGGER_GET_VERSION:
+               if (!(file->f_mode & FMODE_READ)) {
+                       ret = -EBADF;
+                       break;
+               }
+               reader = file->private_data;
+               ret = reader->r_ver;
+               break;
+       case LOGGER_SET_VERSION:
+               if (!(file->f_mode & FMODE_READ)) {
+                       ret = -EBADF;
+                       break;
+               }
+               reader = file->private_data;
+               ret = logger_set_version(reader, argp);
+               break;
        }
 
        mutex_unlock(&log->mutex);
@@ -534,8 +674,8 @@ static const struct file_operations logger_fops = {
 
 /*
  * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which
- * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than
- * LONG_MAX minus LOGGER_ENTRY_MAX_LEN.
+ * must be a power of two, and greater than
+ * (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)).
  */
 #define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \
 static unsigned char _buf_ ## VAR[SIZE]; \
index 2cb06e9..3f612a3 100644 (file)
 #include <linux/types.h>
 #include <linux/ioctl.h>
 
-struct logger_entry {
+/*
+ * The userspace structure for version 1 of the logger_entry ABI.
+ * This structure is returned to userspace unless the caller requests
+ * an upgrade to a newer ABI version.
+ */
+struct user_logger_entry_compat {
        __u16           len;    /* length of the payload */
        __u16           __pad;  /* no matter what, we get 2 bytes of padding */
        __s32           pid;    /* generating process's pid */
@@ -30,14 +35,28 @@ struct logger_entry {
        char            msg[0]; /* the entry's payload */
 };
 
+/*
+ * The structure for version 2 of the logger_entry ABI.
+ * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
+ * is called with version >= 2
+ */
+struct logger_entry {
+       __u16           len;            /* length of the payload */
+       __u16           hdr_size;       /* sizeof(struct logger_entry_v2) */
+       __s32           pid;            /* generating process's pid */
+       __s32           tid;            /* generating process's tid */
+       __s32           sec;            /* seconds since Epoch */
+       __s32           nsec;           /* nanoseconds */
+       uid_t           euid;           /* effective UID of logger */
+       char            msg[0];         /* the entry's payload */
+};
+
 #define LOGGER_LOG_RADIO       "log_radio"     /* radio-related messages */
 #define LOGGER_LOG_EVENTS      "log_events"    /* system/hardware events */
 #define LOGGER_LOG_SYSTEM      "log_system"    /* system/framework messages */
 #define LOGGER_LOG_MAIN                "log_main"      /* everything else */
 
-#define LOGGER_ENTRY_MAX_LEN           (4*1024)
-#define LOGGER_ENTRY_MAX_PAYLOAD       \
-       (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
+#define LOGGER_ENTRY_MAX_PAYLOAD       4076
 
 #define __LOGGERIO     0xAE
 
@@ -45,5 +64,7 @@ struct logger_entry {
 #define LOGGER_GET_LOG_LEN             _IO(__LOGGERIO, 2) /* used log len */
 #define LOGGER_GET_NEXT_ENTRY_LEN      _IO(__LOGGERIO, 3) /* next entry len */
 #define LOGGER_FLUSH_LOG               _IO(__LOGGERIO, 4) /* flush log */
+#define LOGGER_GET_VERSION             _IO(__LOGGERIO, 5) /* abi version */
+#define LOGGER_SET_VERSION             _IO(__LOGGERIO, 6) /* abi version */
 
 #endif /* _LINUX_LOGGER_H */