All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jakub Kicinski <kuba@kernel.org>
To: Li Li <dualli@chromium.org>
Cc: dualli@google.com, corbet@lwn.net, davem@davemloft.net,
	edumazet@google.com, pabeni@redhat.com, donald.hunter@gmail.com,
	gregkh@linuxfoundation.org, arve@android.com, tkjos@android.com,
	maco@android.com, joel@joelfernandes.org, brauner@kernel.org,
	cmllamas@google.com, surenb@google.com, arnd@arndb.de,
	masahiroy@kernel.org, bagasdotme@gmail.com, horms@kernel.org,
	linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
	netdev@vger.kernel.org, hridya@google.com, smoreland@google.com,
	kernel-team@android.com
Subject: Re: [PATCH net-next v7 2/2] binder: report txn errors via generic netlink
Date: Sun, 3 Nov 2024 15:15:54 -0800	[thread overview]
Message-ID: <20241103151554.5fc79ce1@kernel.org> (raw)
In-Reply-To: <20241031092504.840708-3-dualli@chromium.org>

On Thu, 31 Oct 2024 02:25:04 -0700 Li Li wrote:
> +===========================================================
> +Generic Netlink for the Android Binder Driver (Binder Genl)
> +===========================================================
> +
> +The Generic Netlink subsystem in the Linux kernel provides a generic way for
> +the Linux kernel to communicate to the user space applications via binder
> +driver. It is used to report various kinds of binder transactions to user
> +space administration process. The driver allows multiple binder devices and
> +their corresponding binder contexts. Each context has an independent Generic
> +Netlink for security reason. To prevent untrusted user applications from
> +accessing the netlink data, the kernel driver uses unicast mode instead of
> +multicast.
> +
> +Basically, the user space code uses the "set" command to request what kind
> +of binder transactions reported by the kernel binder driver. The driver then

Something is missing here. s/reported/should be reported/ ?

> +uses "reply" command to acknowledge the request. The "set" command also
> +registers the current user space process to receive the reports. When the
> +user space process exits, the previous request will be reset to prevent any
> +potential leaks.
> +
> +Currently the driver can report binder transactions that "failed" to reach
> +the target process, or that are "delayed" due to the target process being
> +frozen by cgroup freezer, or that are considered "spam" according to existing
> +logic in binder_alloc.c.
> +
> +When the specified binder transactions happen, the driver uses the "report"
> +command to send a generic netlink message to the registered process,
> +containing the payload struct binder_report.
> +
> +More details about the flags, attributes and operations can be found at the
> +the doc sections in Documentations/netlink/specs/binder_genl.yaml and the
> +kernel-doc comments of the new source code in binder.{h|c}.
> +
> +Using Binder Genl
> +-----------------
> +
> +The Binder Genl can be used in the same way as any other generic netlink
> +drivers. Userspace application uses a raw netlink socket to send commands
> +to and receive packets from the kernel driver.
> +
> +.. note::
> +    If the userspace application that talks to the driver exits, the kernel
> +    driver will automatically reset the configuration to the default and
> +    stop sending more reports to prevent leaking memory.
> +
> +Usage example (user space pseudo code):
> +
> +::
> +
> +    // open netlink socket
> +    int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
> +
> +    // bind netlink socket
> +    bind(fd, struct socketaddr);
> +
> +    // get the family id of the binder genl
> +    send(fd, CTRL_CMD_GETFAMILY, CTRL_ATTR_FAMILY_NAME, "binder");
> +    void *data = recv(CTRL_CMD_NEWFAMILY);
> +    __u16 id = nla(data)[CTRL_ATTR_FAMILY_ID];
> +
> +    // enable per-context binder report
> +    send(fd, id, BINDER_GENL_CMD_SET, 0, BINDER_GENL_FLAG_FAILED |
> +            BINDER_GENL_FLAG_DELAYED);
> +
> +    // confirm the per-context configuration
> +    void *data = recv(fd, BINDER_GENL_CMD_REPLY);
> +    __u32 pid =  nla(data)[BINDER_GENL_A_CMD_PID];
> +    __u32 flags = nla(data)[BINDER_GENL_A_CMD_FLAGS];
> +
> +    // set optional per-process report, overriding the per-context one
> +    send(fd, id, BINDER_GENL_CMD_SET, getpid(),
> +            BINDER_GENL_FLAG_SPAM | BINDER_REPORT_OVERRIDE);
> +
> +    // confirm the optional per-process configuration
> +    void *data = recv(fd, BINDER_GENL_CMD_REPLY);
> +    __u32 pid =  nla(data)[BINDER_GENL_A_CMD_PID];
> +    __u32 flags = nla(data)[BINDER_GENL_A_CMD_FLAGS];
> +
> +    // wait and read all binder reports
> +    while (running) {
> +            void *data = recv(fd, BINDER_GENL_CMD_REPORT);
> +            u32 *attr = nla(data)[BINDER_GENL_A_REPORT_XXX];
> +
> +            // process binder report
> +            do_something(*attr);
> +    }
> +
> +    // clean up
> +    send(fd, id, BINDER_GENL_CMD_SET, 0, 0);
> +    send(fd, id, BINDER_GENL_CMD_SET, getpid(), 0);
> +    close(fd);
> diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst
> index e85b1adf5908..b3b5cfadffe5 100644
> --- a/Documentation/admin-guide/index.rst
> +++ b/Documentation/admin-guide/index.rst
> @@ -79,6 +79,7 @@ configure specific aspects of kernel behavior to your liking.
>     aoe/index
>     auxdisplay/index
>     bcache
> +   binder_genl
>     binderfs
>     binfmt-misc
>     blockdev/index
> diff --git a/Documentation/netlink/specs/binder_genl.yaml b/Documentation/netlink/specs/binder_genl.yaml
> new file mode 100644
> index 000000000000..35e5f0120fc7
> --- /dev/null
> +++ b/Documentation/netlink/specs/binder_genl.yaml
> @@ -0,0 +1,114 @@
> +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
> +
> +name: binder_genl
> +protocol: genetlink
> +uapi-header: linux/android/binder_genl.h
> +doc: Netlink protocol to report binder transaction errors and warnings.
> +
> +definitions:
> +  -
> +    type: flags
> +    name: flag
> +    doc: |
> +      Used with "set" and "reply" command below, defining what kind of binder
> +      transactions reported to the user space administration process.

Please the references to which commands use given enum and attr.
The YAML specs are automatically rendered as HTML:
https://docs.kernel.org/next/networking/netlink_spec/net_shaper.html
so hopefully it's clear which attrs are used where.

> +    value-start: 0

I thought flags default to starting from 0 (or rather (1<<0))
Please delete this if it's not needed.

> +    entries: [ failed, delayed, spam, override ]
> +
> +attribute-sets:
> +  -
> +    name: cmd
> +    doc: The supported attributes of "set" and "reply" commands
> +    attributes:
> +      -
> +        name: pid
> +        type: u32
> +        doc: |
> +          What binder proc or context to enable binder genl report,
> +          used by "set" and "reply" command below.
> +      -
> +        name: flags
> +        type: u32
> +        doc: |
> +          What kind of binder transactions reported via binder genl,
> +          used by "set" and "reply" command below.
> +  -
> +    name: report
> +    doc: The supported attributes of "report" command
> +    attributes:
> +      -
> +        name: err
> +        type: u32
> +        doc: |
> +          Copy of binder_driver_return_protocol returned to the sender,
> +          used by "report" command below.
> +      -
> +        name: from_pid
> +        type: u32
> +        doc: |
> +          Sender pid of the corresponding binder transaction
> +          used by "report" command below.
> +      -
> +        name: from_tid
> +        type: u32
> +        doc: |
> +          Sender tid of the corresponding binder transaction
> +          used by "report" command below.
> +      -
> +        name: to_pid
> +        type: u32
> +        doc: |
> +          Target pid of the corresponding binder transaction
> +          used by "report" command below.
> +      -
> +        name: to_tid
> +        type: u32
> +        doc: |
> +          Target tid of the corresponding binder transaction
> +          used by "report" command below.
> +      -
> +        name: reply
> +        type: u32
> +        doc: |
> +          1 means the transaction is a reply, 0 otherwise
> +          used by "report" command below.
> +      -
> +        name: flags
> +        type: u32
> +        doc: |
> +          Copy of binder_transaction_data->flags
> +          used by "report" command below.
> +      -
> +        name: code
> +        type: u32
> +        doc: |
> +          Copy of binder_transaction_data->code
> +          used by "report" command below.
> +      -
> +        name: data_size
> +        type: u32
> +        doc: |
> +          Copy of binder_transaction_data->data_size
> +          used by "report" command below.
> +
> +operations:
> +  list:
> +    -
> +      name: set
> +      doc: |
> +        Set flags from user space, requesting what kinds of binder
> +        transactions to report.
> +      attribute-set: cmd
> +
> +      do:
> +        request: &params
> +          attributes:
> +            - pid
> +            - flags
> +        reply: *params
> +    -
> +      name: reply
> +      doc: Acknowledge the above "set" request, echoing the same params.

Hm, this looks strange. We shouldn't need a separate op entry for reply.
Operations are request + response.

> +    -
> +      name: report
> +      doc: Send the requested binder transaction reports to user space.

This is probably an event and contain the list of fields carried:
https://docs.kernel.org/next/userspace-api/netlink/specs.html
The list of fields is needed for user space C and C++ code gen.

> +/**
> + * binder_genl_set_report() - set binder report flags
> + * @context:	the binder context to set the flags
> + * @pid:	the target process
> + * @flags:	the flags to set
> + *
> + * If pid is 0, the flags are applied to the whole binder context.
> + * Otherwise, the flags are applied to the specific process only.
> + */
> +static int binder_genl_set_report(struct binder_context *context, u32 pid,
> +				  u32 flags)
> +{
> +	struct binder_proc *proc;
> +
> +	if (flags != (flags & (BINDER_GENL_FLAG_OVERRIDE
> +			| BINDER_GENL_FLAG_FAILED
> +			| BINDER_GENL_FLAG_DELAYED
> +			| BINDER_GENL_FLAG_SPAM))) {

please add

	enum: flag

to the definition of the flags field in YAML.
Netlink will auto-generate a policy checking that only values defined
in the enum are allowed.

> +		pr_err("Invalid binder report flags: %u\n", flags);
> +		return -EINVAL;
> +	}
> +
> +	if (!pid) {
> +		/* Set the global flags for the whole binder context */
> +		context->report_flags = flags;
> +	} else {
> +		/* Set the per-process flags */
> +		proc = binder_find_proc(pid);
> +		if (!proc) {
> +			pr_err("Invalid binder report pid %u\n", pid);
> +			return -EINVAL;
> +		}
> +
> +		proc->report_flags = flags;
> +	}
> +
> +	return 0;
> +}

> @@ -6311,6 +6500,83 @@ binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
>  	mutex_unlock(&binder_deferred_lock);
>  }
>  
> +/**
> + * binder_genl_nl_set_doit() - .doit handler for BINDER_GENL_CMD_SET
> + * @skb:	the metadata struct passed from netlink driver
> + * @info:	the generic netlink struct passed from netlink driver
> + *
> + * Implements the .doit function to process binder genl commands.
> + */
> +int binder_genl_nl_set_doit(struct sk_buff *skb, struct genl_info *info)
> +{
> +	int payload;
> +	int portid;
> +	u32 pid;
> +	u32 flags;
> +	void *hdr;
> +	struct binder_context *context;
> +
> +	/* Both attributes are required for BINDER_GENL_CMD_SET */
> +	if (!info->attrs[BINDER_GENL_A_CMD_PID] || !info->attrs[BINDER_GENL_A_CMD_FLAGS]) {

Use GENL_REQ_ATTR_CHECK, it will set metadata to let user space know
which attrs are missing

> +		pr_err("Attributes not set\n");

and then you can delete this

> +		return -EINVAL;
> +	}
> +
> +	portid = nlmsg_hdr(skb)->nlmsg_pid;
> +	pid = nla_get_u32(info->attrs[BINDER_GENL_A_CMD_PID]);
> +	flags = nla_get_u32(info->attrs[BINDER_GENL_A_CMD_FLAGS]);
> +	context = container_of(info->family, struct binder_context,
> +			       genl_family);
> +
> +	if (context->report_portid && context->report_portid != portid) {
> +		pr_err("No permission to set report flags from %u\n", portid);

It's better to communicate the errors to application using extack
(inside the netlink reply) using NL_SET_ERR_MSG(info->extack, "yourmsg")

> +		return -EPERM;
> +	}
> +
> +	if (binder_genl_set_report(context, pid, flags) < 0) {
> +		pr_err("Failed to set report flags %u for %u\n", flags, pid);
> +		return -EINVAL;
> +	}
> +
> +	payload = nla_total_size(sizeof(pid)) + nla_total_size(sizeof(flags));
> +	skb = genlmsg_new(payload + GENL_HDRLEN, GFP_KERNEL);
> +	if (!skb) {
> +		pr_err("Failed to alloc binder genl reply message\n");
> +		return -ENOMEM;
> +	}
> +
> +	hdr = genlmsg_put_reply(skb, info, info->family, 0,
> +				BINDER_GENL_CMD_REPLY);

Use the same ID as the request, the BINDER_GENL_CMD_REPLY
shouldn't exist. And then you can use genlmsg_iput().

> +	if (!hdr)
> +		goto free_skb;
> +
> +	if (nla_put_u32(skb, BINDER_GENL_A_CMD_PID, pid))
> +		goto cancel_skb;
> +
> +	if (nla_put_u32(skb, BINDER_GENL_A_CMD_FLAGS, flags))

it's typical to chain all the nla_puts..

	if (nla_put_u32(skb, BINDER_GENL_A_CMD_PID, pid) ||
	    nla_put_u32(skb, BINDER_GENL_A_CMD_FLAGS, flags))

to avoid repeating the goto's for every field.

> +		goto cancel_skb;
> +
> +	genlmsg_end(skb, hdr);
> +
> +	if (genlmsg_reply(skb, info)) {
> +		pr_err("Failed to send binder genl reply message\n");
> +		return -EFAULT;
> +	}
> +
> +	if (!context->report_portid)
> +		context->report_portid = portid;
> +
> +	return 0;
> +
> +cancel_skb:
> +	pr_err("Failed to add reply attributes to binder genl message\n");
> +	genlmsg_cancel(skb, hdr);
> +free_skb:
> +	pr_err("Free binder genl reply message on error\n");
> +	nlmsg_free(skb);
> +	return -EMSGSIZE;
> +}
> +
>  static void print_binder_transaction_ilocked(struct seq_file *m,
>  					     struct binder_proc *proc,
>  					     const char *prefix,
> @@ -6894,6 +7160,28 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
>  	{} /* terminator */
>  };
>  
> +/**
> + * binder_genl_init() - initialize binder generic netlink
> + * @family:	the generic netlink family
> + * @name:	the binder device name
> + *
> + * Registers the binder generic netlink family.
> + */
> +int binder_genl_init(struct genl_family *family, const char *name)
> +{
> +	int ret;
> +
> +	memcpy(family, &binder_genl_nl_family, sizeof(*family));
> +	strscpy(family->name, name, GENL_NAMSIZ);

You're trying to register multiple families with different names?
The family defines the language / protocol. If you have multiple
entities to multiplex you should do that based on attributes inside
the messages.

> +	ret = genl_register_family(family);
> +	if (ret) {
> +		pr_err("Failed to register binder genl: %s\n", name);
> +		return ret;
> +	}
> +
> +	return 0;
> +}


  reply	other threads:[~2024-11-03 23:15 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-31  9:25 [PATCH net-next v7 0/2] binder: report txn errors via generic netlink Li Li
2024-10-31  9:25 ` [PATCH net-next v7 1/2] tools: ynl-gen: allow uapi headers in sub-dirs Li Li
2024-10-31  9:25 ` [PATCH net-next v7 2/2] binder: report txn errors via generic netlink Li Li
2024-11-03 23:15   ` Jakub Kicinski [this message]
2024-11-04  6:25     ` Li Li
2024-11-04 16:19       ` Jakub Kicinski
2024-11-04 17:12         ` Li Li
2024-11-05  2:41           ` Jakub Kicinski
2024-11-05 21:00             ` Li Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241103151554.5fc79ce1@kernel.org \
    --to=kuba@kernel.org \
    --cc=arnd@arndb.de \
    --cc=arve@android.com \
    --cc=bagasdotme@gmail.com \
    --cc=brauner@kernel.org \
    --cc=cmllamas@google.com \
    --cc=corbet@lwn.net \
    --cc=davem@davemloft.net \
    --cc=donald.hunter@gmail.com \
    --cc=dualli@chromium.org \
    --cc=dualli@google.com \
    --cc=edumazet@google.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=horms@kernel.org \
    --cc=hridya@google.com \
    --cc=joel@joelfernandes.org \
    --cc=kernel-team@android.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=maco@android.com \
    --cc=masahiroy@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=smoreland@google.com \
    --cc=surenb@google.com \
    --cc=tkjos@android.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.