printk: check for user pointers in format string parameters
[akaros.git] / kern / src / fdtap.c
1 /* Copyright (c) 2015 Google Inc
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * FD taps.  Allows the user to receive events when certain things happen to an
6  * FD's underlying device file/qid. */
7
8 #include <fdtap.h>
9 #include <event.h>
10 #include <kmalloc.h>
11 #include <syscall.h>
12 #include <error.h>
13 #include <umem.h>
14
15 static void tap_min_release(struct kref *kref)
16 {
17         struct fd_tap *tap = container_of(kref, struct fd_tap, kref);
18
19         cclose(tap->chan);
20         kfree(tap);
21 }
22
23 static void tap_full_release(struct kref *kref)
24 {
25         struct fd_tap *tap = container_of(kref, struct fd_tap, kref);
26
27         devtab[tap->chan->type].tapfd(tap->chan, tap, FDTAP_CMD_REM);
28         tap_min_release(kref);
29 }
30
31 /* Adds a tap with the file/qid of the underlying device for the requested FD.
32  * The FD must be a chan, and the device must support the filter requested.
33  *
34  * Returns -1 or some other device-specific non-zero number on failure, 0 on
35  * success. */
36 int add_fd_tap(struct proc *p, struct fd_tap_req *tap_req)
37 {
38         struct fd_table *fdt = &p->open_files;
39         struct fd_tap *tap;
40         int ret = 0;
41         struct chan *chan;
42         int fd = tap_req->fd;
43
44         if (fd < 0) {
45                 set_errno(EBADF);
46                 return -1;
47         }
48         tap = kzmalloc(sizeof(struct fd_tap), MEM_WAIT);
49         tap->proc = p;
50         tap->fd = fd;
51         tap->filter = tap_req->filter;
52         tap->ev_q = tap_req->ev_q;
53         tap->ev_id = tap_req->ev_id;
54         tap->data = tap_req->data;
55         if (!is_user_rwaddr(tap->ev_q, sizeof(struct event_queue))) {
56                 set_error(EINVAL, "Tap request with bad event_queue %p",
57                           tap->ev_q);
58                 kfree(tap);
59                 return -1;
60         }
61         spin_lock(&fdt->lock);
62         if (fd >= fdt->max_fdset) {
63                 set_errno(ENFILE);
64                 goto out_with_lock;
65         }
66         if (!GET_BITMASK_BIT(fdt->open_fds->fds_bits, fd)) {
67                 set_errno(EBADF);
68                 goto out_with_lock;
69         }
70         if (!fdt->fd[fd].fd_chan) {
71                 set_error(EINVAL, "Can't tap a VFS file");
72                 goto out_with_lock;
73         }
74         chan = fdt->fd[fd].fd_chan;
75         if (fdt->fd[fd].fd_tap) {
76                 set_error(EBUSY, "FD %d already has a tap", fd);
77                 goto out_with_lock;
78         }
79         if (!devtab[chan->type].tapfd) {
80                 set_error(ENOSYS, "Device %s does not handle taps",
81                                   devtab[chan->type].name);
82                 goto out_with_lock;
83         }
84         /* need to keep chan alive for our call to the device.  someone else
85          * could come in and close the FD and the chan, once we unlock */
86         chan_incref(chan);
87         tap->chan = chan;
88         /* One for the FD table, one for us to keep the removal of *this* tap
89          * from happening until we've attempted to register with the device. */
90         kref_init(&tap->kref, tap_full_release, 2);
91         fdt->fd[fd].fd_tap = tap;
92         /* As soon as we unlock, another thread can come in and remove our old
93          * tap from the table and decref it.  Our ref keeps us from removing it
94          * yet, as well as keeps the memory safe.  However, a new tap can be
95          * installed and registered with the device before we even attempt to
96          * register.  The devices should be able to handle multiple, distinct
97          * taps, even if they happen to have the same {proc, fd} tuple. */
98         spin_unlock(&fdt->lock);
99         /* For refcnting fans, the tap ref is weak/uncounted.  We'll protect the
100          * memory and call the device when tap is being released. */
101         ret = devtab[chan->type].tapfd(chan, tap, FDTAP_CMD_ADD);
102         if (ret) {
103                 /* we failed, so we need to make sure *our* tap is removed.  We
104                  * haven't decreffed, so we know our tap pointer is unique. */
105                 spin_lock(&fdt->lock);
106                 if (fdt->fd[fd].fd_tap == tap) {
107                         fdt->fd[fd].fd_tap = 0;
108                         /* normally we can't decref a tap while holding a lock,
109                          * but we know we have another reference so this won't
110                          * trigger a release */
111                         kref_put(&tap->kref);
112                 }
113                 spin_unlock(&fdt->lock);
114                 /* Regardless of whether someone else removed it or not, *we*
115                  * are the only ones that know that registration failed and that
116                  * we shouldn't remove it.  Since we still hold a ref, we can
117                  * change the release method to skip the device dereg. */
118                 tap->kref.release = tap_min_release;
119         }
120         kref_put(&tap->kref);
121         return ret;
122 out_with_lock:
123         spin_unlock(&fdt->lock);
124         kfree(tap);
125         return -1;
126 }
127
128 /* Removes the FD tap associated with FD.  Returns 0 on success, -1 with
129  * errno/errstr on failure. */
130 int remove_fd_tap(struct proc *p, int fd)
131 {
132         struct fd_table *fdt = &p->open_files;
133         struct fd_tap *tap;
134
135         if (fd < 0) {
136                 set_errno(EBADF);
137                 return -1;
138         }
139         spin_lock(&fdt->lock);
140         if (fd >= fdt->max_fdset) {
141                 set_errno(ENFILE);
142                 goto err_with_lock;
143         }
144         tap = fdt->fd[fd].fd_tap;
145         if (!tap) {
146                 set_error(EBADF, "FD %d was not tapped", fd);
147                 goto err_with_lock;
148         }
149         fdt->fd[fd].fd_tap = 0;
150         spin_unlock(&fdt->lock);
151         kref_put(&tap->kref);
152         return 0;
153 err_with_lock:
154         spin_unlock(&fdt->lock);
155         return -1;
156 }
157
158 /* Fires off tap, with the events of filter having occurred.  Returns -1 on
159  * error, though this need a little more thought.
160  *
161  * Some callers may require this to not block. */
162 int fire_tap(struct fd_tap *tap, int filter)
163 {
164         ERRSTACK(1);
165         struct event_msg ev_msg = {0};
166         int fire_filt = tap->filter & filter;
167
168         if (!fire_filt)
169                 return 0;
170         if (waserror()) {
171                 /* The process owning the tap could trigger a kernel PF, as with
172                  * any send_event() call.  Eventually we'll catch that with
173                  * waserror. */
174                 warn("Tap for proc %d, fd %d, threw %s", tap->proc->pid,
175                      tap->fd, current_errstr());
176                 poperror();
177                 return -1;
178         }
179         ev_msg.ev_type = tap->ev_id;    /* e.g. CEQ idx */
180         ev_msg.ev_arg2 = fire_filt;     /* e.g. CEQ coalesce */
181         ev_msg.ev_arg3 = tap->data;     /* e.g. CEQ data */
182         send_event(tap->proc, tap->ev_q, &ev_msg, 0);
183         poperror();
184         return 0;
185 }