struct kdbus_queue_entry *entry = NULL;
int ret;
- /* Reject unknown flags */
- if (recv->flags & ~(KDBUS_RECV_PEEK |
- KDBUS_RECV_DROP |
- KDBUS_RECV_USE_PRIORITY))
- return -EOPNOTSUPP;
-
if (recv->offset > 0)
return -EINVAL;
mutex_lock(&conn->lock);
- ret = kdbus_queue_entry_peek(&conn->queue,
- recv->priority,
+ ret = kdbus_queue_entry_peek(&conn->queue, recv->priority,
recv->flags & KDBUS_RECV_USE_PRIORITY,
&entry);
if (ret < 0)
int ret = 0;
u64 flags;
- if (cmd_info->flags & ~_KDBUS_ATTACH_ALL)
- return -EOPNOTSUPP;
-
if (cmd_info->id == 0) {
const char *name;
u64 attach_flags;
int ret;
- if (cmd->flags != 0)
- return -EOPNOTSUPP;
-
KDBUS_ITEMS_FOREACH(item, cmd->items, KDBUS_ITEMS_SIZE(cmd, items)) {
switch (item->type) {
case KDBUS_ITEM_ATTACH_FLAGS:
BUG_ON(*c);
- /* Reject unknown flags */
- if (hello->conn_flags & ~(KDBUS_HELLO_ACCEPT_FD |
- KDBUS_HELLO_ACTIVATOR |
- KDBUS_HELLO_POLICY_HOLDER |
- KDBUS_HELLO_MONITOR))
- return -EOPNOTSUPP;
-
is_monitor = hello->conn_flags & KDBUS_HELLO_MONITOR;
is_activator = hello->conn_flags & KDBUS_HELLO_ACTIVATOR;
is_policy_holder = hello->conn_flags & KDBUS_HELLO_POLICY_HOLDER;
make = p;
+ ret = kdbus_negotiate_flags(make->flags, buf,
+ offsetof(typeof(*make), flags),
+ KDBUS_MAKE_ACCESS_GROUP |
+ KDBUS_MAKE_ACCESS_WORLD);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(make->items,
KDBUS_ITEMS_SIZE(make, items));
if (ret < 0)
if (ret < 0)
break;
- /* Reject unknown flags */
- if (make->flags & ~(KDBUS_MAKE_ACCESS_GROUP |
- KDBUS_MAKE_ACCESS_WORLD)) {
- ret = -EOPNOTSUPP;
- break;
- }
-
if (make->flags & KDBUS_MAKE_ACCESS_WORLD) {
mode = 0666;
} else if (make->flags & KDBUS_MAKE_ACCESS_GROUP) {
make = p;
+ ret = kdbus_negotiate_flags(make->flags, buf,
+ offsetof(typeof(*make), flags),
+ KDBUS_MAKE_ACCESS_GROUP |
+ KDBUS_MAKE_ACCESS_WORLD);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(make->items,
KDBUS_ITEMS_SIZE(make, items));
if (ret < 0)
if (ret < 0)
break;
- /* Reject unknown flags */
- if (make->flags & ~(KDBUS_MAKE_ACCESS_GROUP |
- KDBUS_MAKE_ACCESS_WORLD)) {
- ret = -EOPNOTSUPP;
- break;
- }
-
if (make->flags & KDBUS_MAKE_ACCESS_WORLD)
mode = 0666;
make = p;
+ ret = kdbus_negotiate_flags(make->flags, buf,
+ offsetof(typeof(*make), flags),
+ KDBUS_MAKE_ACCESS_GROUP |
+ KDBUS_MAKE_ACCESS_WORLD);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(make->items,
KDBUS_ITEMS_SIZE(make, items));
if (ret < 0)
} else if (make->flags & KDBUS_MAKE_ACCESS_GROUP) {
mode = 0660;
gid = current_fsgid();
- } else if (make->flags) {
- ret = -EOPNOTSUPP;
- break;
}
/* custom endpoints always have a policy db */
hello = p;
+ ret = kdbus_negotiate_flags(hello->conn_flags, buf,
+ offsetof(struct kdbus_cmd_hello,
+ conn_flags),
+ KDBUS_HELLO_ACCEPT_FD |
+ KDBUS_HELLO_ACTIVATOR |
+ KDBUS_HELLO_POLICY_HOLDER |
+ KDBUS_HELLO_MONITOR);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(hello->items,
KDBUS_ITEMS_SIZE(hello, items));
if (ret < 0)
cmd_name = p;
+ ret = kdbus_negotiate_flags(cmd_name->flags, buf,
+ offsetof(typeof(*cmd_name), flags),
+ KDBUS_NAME_REPLACE_EXISTING |
+ KDBUS_NAME_ALLOW_REPLACEMENT |
+ KDBUS_NAME_QUEUE);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(cmd_name->items,
KDBUS_ITEMS_SIZE(cmd_name, items));
if (ret < 0)
cmd_name = p;
+ ret = kdbus_negotiate_flags(cmd_name->flags, buf,
+ offsetof(typeof(*cmd_name), flags),
+ 0);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(cmd_name->items,
KDBUS_ITEMS_SIZE(cmd_name, items));
if (ret < 0)
break;
}
+ ret = kdbus_negotiate_flags(cmd_list.flags, buf,
+ offsetof(typeof(cmd_list), flags),
+ KDBUS_NAME_LIST_UNIQUE |
+ KDBUS_NAME_LIST_NAMES |
+ KDBUS_NAME_LIST_ACTIVATORS |
+ KDBUS_NAME_LIST_QUEUED);
+ if (ret < 0)
+ break;
+
ret = kdbus_cmd_name_list(conn->bus->name_registry,
conn, &cmd_list);
if (ret < 0)
break;
cmd_info = p;
+
+ ret = kdbus_negotiate_flags(cmd_info->flags, buf,
+ offsetof(typeof(*cmd_info), flags),
+ _KDBUS_ATTACH_ALL);
+ if (ret < 0)
+ break;
+
ret = kdbus_cmd_conn_info(conn, cmd_info);
if (ret < 0)
break;
cmd_update = p;
+ ret = kdbus_negotiate_flags(cmd_update->flags, buf,
+ offsetof(typeof(*cmd_update),
+ flags),
+ 0);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(cmd_update->items,
KDBUS_ITEMS_SIZE(cmd_update, items));
if (ret < 0)
cmd_match = p;
+ ret = kdbus_negotiate_flags(cmd_match->flags, buf,
+ offsetof(typeof(*cmd_match),
+ flags),
+ KDBUS_MATCH_REPLACE);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(cmd_match->items,
KDBUS_ITEMS_SIZE(cmd_match, items));
if (ret < 0)
cmd_match = p;
+ ret = kdbus_negotiate_flags(cmd_match->flags, buf,
+ offsetof(typeof(*cmd_match),
+ flags),
+ 0);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(cmd_match->items,
KDBUS_ITEMS_SIZE(cmd_match, items));
if (ret < 0)
break;
}
- /* handle a queued message */
ret = kdbus_copy_from_user(&cmd_recv, buf, sizeof(cmd_recv));
if (ret < 0)
break;
+ ret = kdbus_negotiate_flags(cmd_recv.flags, buf,
+ offsetof(typeof(cmd_recv), flags),
+ KDBUS_RECV_PEEK | KDBUS_RECV_DROP |
+ KDBUS_RECV_USE_PRIORITY);
+ if (ret < 0)
+ break;
+
ret = kdbus_cmd_msg_recv(conn, &cmd_recv);
if (ret < 0)
break;
if (ret < 0)
break;
- if (cmd_free.flags != 0)
- return -EOPNOTSUPP;
+ ret = kdbus_negotiate_flags(cmd_free.flags, buf,
+ offsetof(typeof(cmd_free), flags),
+ 0);
+ if (ret < 0)
+ break;
ret = kdbus_pool_release_offset(conn->pool, cmd_free.offset);
break;
cmd_update = p;
+ ret = kdbus_negotiate_flags(cmd_update->flags, buf,
+ offsetof(typeof(*cmd_update),
+ flags),
+ 0);
+ if (ret < 0)
+ break;
+
ret = kdbus_items_validate(cmd_update->items,
KDBUS_ITEMS_SIZE(cmd_update, items));
if (ret < 0)
#define KDBUS_DST_ID_NAME (0)
#define KDBUS_MATCH_ID_ANY (~0ULL)
#define KDBUS_DST_ID_BROADCAST (~0ULL)
+#define KDBUS_FLAG_KERNEL (1ULL << 63)
/**
* struct kdbus_notify_id_change - name registry change message
use should be limited to the absolute minimum for the same reason.
-3. Data Structures
+3. Data Structures and flags
===============================================================================
+3.1 Data structures and interconnections
+----------------------------------------
+
+-------------------------------------------------------------------------+
| Domain (Init Domain) |
| /dev/kdbus/control |
use that notation, neither internally nor externally. However, libraries and
other usespace code that aims for compatibility to D-Bus might.
+3.2 Flags
+---------
+
+All ioctls used in the communication with the driver contain a 64-bit flags
+field. All bits that are not recognized by the kernel are rejected, and the
+ioctl fails with -EINVAL. Regardless of whether the kernel accepts the
+provided flags or not, the flags field in the ioctl buffer will be updated with
+all the bits the kernel driver knows about in its current state, and set the
+highest bit (KDBUS_FLAGS_KERNEL) as well. Userspace can use the returned value
+to negotiate features. The KDBUS_FLAGS_KERNEL bit will never be valid in any
+flags field of any command, so setting it will always make the ioctl fail.
+Hence, this is a way to probe possible kernel features.
+
4. Items
===============================================================================
For KDBUS_CMD_BUS_MAKE:
- -EOPNOTSUPP The flags supplied in the kdbus_cmd_make struct are invalid
- -EINVAL The supplied name does not start with the current uid and a '-'
+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid or
+ the supplied name does not start with the current uid and a '-'
-EEXIST A bus of that name already exists
-ESHUTDOWN The domain for the bus is already shut down
-EMFILE The maximum number of buses for the current user is exhausted
-KDBUS_CMD_DOMAIN_MAKE
+For KDBUS_CMD_DOMAIN_MAKE:
- -EPERM The calling user does not have CAP_IPC_OWNER set
- -EOPNOTSUPP The flags supplied in the kdbus_cmd_make struct are invalid
- -EINVAL No name supplied for top-level domain
+ -EPERM The calling user does not have CAP_IPC_OWNER set, or
+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid, or
+ no name supplied for top-level domain
-EEXIST A domain of that name already exists
-KDBUS_CMD_ENDPOINT_MAKE
+For KDBUS_CMD_ENDPOINT_MAKE:
-EPERM The calling user is not privileged (see Terminology)
- -EOPNOTSUPP The flags supplied in the kdbus_cmd_make struct are invalid
+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid
-EEXIST An endpoint of that name already exists
-KDBUS_CMD_HELLO
+For KDBUS_CMD_HELLO:
- -EOPNOTSUPP The flags supplied in the kdbus_cmd_make struct are invalid
-EFAULT The supplied pool size was 0 or not a multiple of the page size
- -EINVAL An illegal combination of KDBUS_HELLO_MONITOR,
+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid, or
+ an illegal combination of KDBUS_HELLO_MONITOR,
KDBUS_HELLO_ACTIVATOR and KDBUS_HELLO_POLICY_HOLDER was passed
in the flags, or an invalid set of items was supplied
-EPERM An KDBUS_ITEM_CREDS items was supplied, but the current user is
-ESHUTDOWN The bus has already been shut down
-EMFILE The maximum number of connection on the bus has been reached
-KDBUS_CMD_BYEBYE
+For KDBUS_CMD_BYEBYE:
-EALREADY The connection has already been shut down
-EBUSY There are still messages queued up in the connection's pool
-KDBUS_CMD_MSG_SEND
+For KDBUS_CMD_MSG_SEND:
-EOPNOTSUPP The connection is unconnected, or a fd was passed that is
either a kdbus handle itself or a unix domain socket. Both is
For KDBUS_CMD_MSG_RECV:
- -EINVAL The offset parameter was != 0 when entering the ioctl
+ -EINVAL Invalid flags or offset
-EAGAIN No message found in the queue
-ENOMSG No message of the requested priority found
For KDBUS_CMD_MSG_CANCEL:
+ -EINVAL Invalid flags
-ENOENT Pending message with the supplied cookie not found
For KDBUS_CMD_FREE:
-ENXIO No pool slice found at given offset
- -EINVAL The offset is valid, but the user is not allowed to free the
- slice. This happens, for example, if the offset was retrieved
- with KDBUS_RECV_PEEK.
+ -EINVAL Invalid flags provided, the offset is valid, but the user is
+ not allowed to free the slice. This happens, for example, if
+ the offset was retrieved with KDBUS_RECV_PEEK.
For KDBUS_CMD_NAME_ACQUIRE:
For KDBUS_CMD_NAME_RELEASE:
+ -EINVAL Invalid command flags provided
-ESRCH Name is not found found in the registry
-EADDRINUSE Name is owned by a different connection and can't be released
For KDBUS_CMD_NAME_LIST:
+
+ -EINVAL Invalid flags
-ENOBUFS No available memory in the connection's pool.
For KDBUS_CMD_CONN_INFO:
- -EINVAL Neither an ID nor a name was provided, or the name is not
- valid.
+
+ -EINVAL Invalid flags, or neither an ID nor a name was provided,
+ or the name is invalid.
-ESRCH Connection lookup by name failed
-ENXIO No connection with the provided number connection ID found
For KDBUS_CMD_CONN_UPDATE:
- -EINVAL Invalid item attached
+ -EINVAL Illegal flags or items
-EOPNOTSUPP Connection is not attached to bus
-E2BIG Too many policy items attached
-EINVAL Wildcards submitted in policy entries, or illegal sequence
For KDBUS_CMD_ENDPOINT_UPDATE:
-E2BIG Too many policy items attached
- -EINVAL Wildcards submitted in policy entries, or illegal sequence
- of policy items
+ -EINVAL Invalid flags, or wildcards submitted in policy entries,
+ or illegal sequence of policy items
For KDBUS_CMD_MATCH_ADD:
- -EINVAL Illegal items
+ -EINVAL Illegal flags or items
-EDOM Illegal bloom filter size
-EMFILE Too many matches for this connection
For KDBUS_CMD_MATCH_REMOVE:
+ -EINVAL Illegal flags
-ENOENT A match entry with the given cookie could not be found.
lockdep_assert_held(conn);
- if (cmd->flags != 0)
- return -EOPNOTSUPP;
-
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
ret = -ENOMEM;
lockdep_assert_held(conn);
- if (cmd->flags != 0)
- return -EOPNOTSUPP;
-
mutex_lock(&db->entries_lock);
ret = __kdbus_match_db_remove_unlocked(db, cmd->cookie);
mutex_unlock(&db->entries_lock);
* Return: 0 on success, negative errno on failure.
*/
int kdbus_kmsg_new_from_user(struct kdbus_conn *conn,
- const struct kdbus_msg __user *msg,
+ struct kdbus_msg __user *msg,
struct kdbus_kmsg **kmsg)
{
struct kdbus_kmsg *m;
goto exit_free;
}
- /* Reject unknown flags */
- if (m->msg.flags & ~(KDBUS_MSG_FLAGS_EXPECT_REPLY |
- KDBUS_MSG_FLAGS_SYNC_REPLY |
- KDBUS_MSG_FLAGS_NO_AUTO_START)) {
- ret = -EOPNOTSUPP;
+ ret = kdbus_negotiate_flags(m->msg.flags, msg,
+ offsetof(struct kdbus_msg, flags),
+ KDBUS_MSG_FLAGS_EXPECT_REPLY |
+ KDBUS_MSG_FLAGS_SYNC_REPLY |
+ KDBUS_MSG_FLAGS_NO_AUTO_START);
+ if (ret < 0)
goto exit_free;
- }
if (m->msg.flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) {
/* requests for replies need a timeout */
int kdbus_kmsg_new(size_t extra_size, struct kdbus_kmsg **kmsg);
int kdbus_kmsg_new_from_user(struct kdbus_conn *conn,
- const struct kdbus_msg __user *msg,
+ struct kdbus_msg __user *msg,
struct kdbus_kmsg **kmsg);
void kdbus_kmsg_free(struct kdbus_kmsg *kmsg);
#endif
const char *name;
int ret;
- if (cmd->flags & ~(KDBUS_NAME_REPLACE_EXISTING |
- KDBUS_NAME_ALLOW_REPLACEMENT |
- KDBUS_NAME_QUEUE))
- return -EOPNOTSUPP;
-
ret = kdbus_items_get_str(cmd->items, KDBUS_ITEMS_SIZE(cmd, items),
KDBUS_ITEM_NAME, &name);
if (ret < 0)
int ret;
const char *name;
- if (cmd->flags != 0)
- return -EOPNOTSUPP;
-
ret = kdbus_items_get_str(cmd->items, KDBUS_ITEMS_SIZE(cmd, items),
KDBUS_ITEM_NAME, &name);
if (ret < 0)
policy_db = &conn->ep->policy_db;
- /* Reject unknown flags */
- if (cmd->flags & ~(KDBUS_NAME_LIST_UNIQUE |
- KDBUS_NAME_LIST_NAMES |
- KDBUS_NAME_LIST_ACTIVATORS |
- KDBUS_NAME_LIST_QUEUED))
- return -EOPNOTSUPP;
-
/* lock order: domain -> bus -> ep -> names -> conn */
down_read(&conn->bus->conn_rwlock);
down_read(®->rwlock);
bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
bus_make.head.size = sizeof(struct kdbus_cmd_make) +
sizeof(bus_make.bs) + bus_make.n_size;
+ bus_make.head.flags = 0;
ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make);
ASSERT_RETURN(ret == -1 && errno == EINVAL);
bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
bus_make.head.size = sizeof(struct kdbus_cmd_make) +
sizeof(bus_make.bs) + bus_make.n_size;
+ bus_make.head.flags = 0;
ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make);
ASSERT_RETURN(ret == -1 && errno == EINVAL);
bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
bus_make.head.size = sizeof(struct kdbus_cmd_make) +
sizeof(bus_make.bs) + bus_make.n_size;
+ bus_make.head.flags = 0;
ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make);
ASSERT_RETURN(ret == -1 && errno == EINVAL);
bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
bus_make.head.size = sizeof(struct kdbus_cmd_make) +
sizeof(bus_make.bs) + bus_make.n_size;
+ bus_make.head.flags = 0;
ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make);
ASSERT_RETURN(ret == 0);
snprintf(s, sizeof(s), "/dev/" KBUILD_MODNAME "/%u-blah-1/bus", uid);
/* a size of 0 must return EMSGSIZE */
hello.size = 1;
+ hello.conn_flags = KDBUS_HELLO_ACCEPT_FD;
ret = ioctl(fd, KDBUS_CMD_HELLO, &hello);
ASSERT_RETURN(ret == -1 && errno == EINVAL);
/* check faulty flags */
hello.conn_flags = 1ULL << 32;
ret = ioctl(fd, KDBUS_CMD_HELLO, &hello);
- ASSERT_RETURN(ret == -1 && errno == EOPNOTSUPP);
+ ASSERT_RETURN(ret == -1 && errno == EINVAL);
+
+ /* kernel must have set its bit in the ioctl buffer */
+ ASSERT_RETURN(hello.conn_flags & KDBUS_FLAG_KERNEL);
hello.conn_flags = KDBUS_HELLO_ACCEPT_FD;
/* check for faulty pool sizes */
hello.pool_size = 0;
+ hello.conn_flags = KDBUS_HELLO_ACCEPT_FD;
ret = ioctl(fd, KDBUS_CMD_HELLO, &hello);
ASSERT_RETURN(ret == -1 && errno == EFAULT);
hello.pool_size = 4097;
+ hello.conn_flags = KDBUS_HELLO_ACCEPT_FD;
ret = ioctl(fd, KDBUS_CMD_HELLO, &hello);
ASSERT_RETURN(ret == -1 && errno == EFAULT);
hello.pool_size = POOL_SIZE;
/* success test */
+ hello.conn_flags = KDBUS_HELLO_ACCEPT_FD;
ret = ioctl(fd, KDBUS_CMD_HELLO, &hello);
ASSERT_RETURN(ret == 0);
snprintf(domain_make.name, sizeof(domain_make.name), "blah");
domain_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(domain_make.name) + 1;
domain_make.head.size = sizeof(struct kdbus_cmd_make) + domain_make.n_size;
+ domain_make.head.flags = 0;
ret = ioctl(fd, KDBUS_CMD_DOMAIN_MAKE, &domain_make);
if (ret < 0 && errno == EPERM)
return TEST_SKIP;
F_OK) == 0);
/* can't use the same fd for domain make twice */
+ domain_make.head.flags = 0;
ret = ioctl(fd, KDBUS_CMD_DOMAIN_MAKE, &domain_make);
ASSERT_RETURN(ret == -1 && errno == EBADFD);
/* can't register the same name twice */
fd2 = open("/dev/" KBUILD_MODNAME "/control", O_RDWR|O_CLOEXEC);
+ domain_make.head.flags = 0;
ret = ioctl(fd2, KDBUS_CMD_DOMAIN_MAKE, &domain_make);
ASSERT_RETURN(ret == -1 && errno == EEXIST);
close(fd2);
int ret;
struct kdbus_cmd_free cmd_free;
+ /* free an unallocated buffer */
cmd_free.flags = 0;
cmd_free.offset = 0;
-
- /* free an unallocated buffer */
ret = ioctl(env->conn->fd, KDBUS_CMD_FREE, &cmd_free);
ASSERT_RETURN(ret == -1 && errno == ENXIO);
/* free a buffer out of the pool's bounds */
+ cmd_free.flags = 0;
cmd_free.offset = POOL_SIZE + 1;
ret = ioctl(env->conn->fd, KDBUS_CMD_FREE, &cmd_free);
ASSERT_RETURN(ret == -1 && errno == ENXIO);
#include <linux/ctype.h>
#include <linux/string.h>
+#include <linux/uaccess.h>
#include "limits.h"
#include "util.h"
return 0;
}
+
+/**
+ * kdbus_negotiate_flags() - check flags provided by user, and write the
+ * valid mask back
+ * @flags: The flags mask provided by userspace
+ * @buf: The buffer provided by userspace
+ * @offset: Offset of the flags field inside the user-provided struct
+ * @valid: Mask of valid bits
+ *
+ * This function will check whether the flags provided by userspace are within
+ * the combination of allowed bits to the kernel, with the KDBUS_FLAGS_KERNEL
+ * bit set in the return buffer.
+ *
+ * Return: 0 on success, -EFAULT if copy_to_user() failed, or -EINVAL if
+ * userspace submitted invalid bits in its mask.
+ */
+int kdbus_negotiate_flags(u64 flags, void __user *buf, off_t offset, u64 valid)
+{
+ u64 val = valid | KDBUS_FLAG_KERNEL;
+
+ /*
+ * KDBUS_FLAG_KERNEL is reserved. Make sure it is never considered
+ * valid by any user of this function.
+ */
+ BUG_ON(valid & KDBUS_FLAG_KERNEL);
+
+ if (copy_to_user(((u8 __user *) buf) + offset, &val, sizeof(val)))
+ return -EFAULT;
+
+ if (flags & ~valid)
+ return -EINVAL;
+
+ return 0;
+}
}
int kdbus_sysname_is_valid(const char *name);
+int kdbus_negotiate_flags(u64 flags, void __user *buf, off_t offset, u64 valid);
#endif