lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20240731143946.f5689502e03b9ff2e79be494@kernel.org>
Date: Wed, 31 Jul 2024 14:39:46 +0900
From: Masami Hiramatsu (Google) <mhiramat@...nel.org>
To: Oleg Nesterov <oleg@...hat.com>
Cc: andrii@...nel.org, peterz@...radead.org, jolsa@...nel.org,
 rostedt@...dmis.org, linux-kernel@...r.kernel.org,
 linux-trace-kernel@...r.kernel.org
Subject: Re: [PATCH v2 5/5] uprobes: make uprobe_register() return struct
 uprobe *

On Mon, 29 Jul 2024 15:45:35 +0200
Oleg Nesterov <oleg@...hat.com> wrote:

> This way uprobe_unregister() and uprobe_apply() can use "struct uprobe *"
> rather than inode + offset. This simplifies the code and allows to avoid
> the unnecessary find_uprobe() + put_uprobe() in these functions.
> 
> TODO: uprobe_unregister() still needs get_uprobe/put_uprobe to ensure that
> this uprobe can't be freed before up_write(&uprobe->register_rwsem).

Is this TODO item, or just a note? At this moment, this is natural
to use get_uprobe() to protect uprobe itself.

Thank you,

> 
> Signed-off-by: Oleg Nesterov <oleg@...hat.com>
> Acked-by: Andrii Nakryiko <andrii@...nel.org>
> ---
>  include/linux/uprobes.h     | 15 +++++-----
>  kernel/events/uprobes.c     | 56 +++++++++++++++----------------------
>  kernel/trace/bpf_trace.c    | 25 ++++++++---------
>  kernel/trace/trace_uprobe.c | 26 ++++++++---------
>  4 files changed, 55 insertions(+), 67 deletions(-)
> 
> diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
> index 440316fbf3c6..137ddfc0b2f8 100644
> --- a/include/linux/uprobes.h
> +++ b/include/linux/uprobes.h
> @@ -16,6 +16,7 @@
>  #include <linux/types.h>
>  #include <linux/wait.h>
>  
> +struct uprobe;
>  struct vm_area_struct;
>  struct mm_struct;
>  struct inode;
> @@ -110,9 +111,9 @@ extern bool is_trap_insn(uprobe_opcode_t *insn);
>  extern unsigned long uprobe_get_swbp_addr(struct pt_regs *regs);
>  extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs);
>  extern int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t);
> -extern int uprobe_register(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc);
> -extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool);
> -extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
> +extern struct uprobe *uprobe_register(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc);
> +extern int uprobe_apply(struct uprobe *uprobe, struct uprobe_consumer *uc, bool);
> +extern void uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc);
>  extern int uprobe_mmap(struct vm_area_struct *vma);
>  extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end);
>  extern void uprobe_start_dup_mmap(void);
> @@ -150,18 +151,18 @@ static inline void uprobes_init(void)
>  
>  #define uprobe_get_trap_addr(regs)	instruction_pointer(regs)
>  
> -static inline int
> +static inline struct uprobe *
>  uprobe_register(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc)
>  {
> -	return -ENOSYS;
> +	return ERR_PTR(-ENOSYS);
>  }
>  static inline int
> -uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool add)
> +uprobe_apply(struct uprobe* uprobe, struct uprobe_consumer *uc, bool add)
>  {
>  	return -ENOSYS;
>  }
>  static inline void
> -uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
> +uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
>  {
>  }
>  static inline int uprobe_mmap(struct vm_area_struct *vma)
> diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
> index b7f40bad8abc..974474680820 100644
> --- a/kernel/events/uprobes.c
> +++ b/kernel/events/uprobes.c
> @@ -1099,20 +1099,14 @@ __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
>  		delete_uprobe(uprobe);
>  }
>  
> -/*
> +/**
>   * uprobe_unregister - unregister an already registered probe.
> - * @inode: the file in which the probe has to be removed.
> - * @offset: offset from the start of the file.
> + * @uprobe: uprobe to remove
>   * @uc: identify which probe if multiple probes are colocated.
>   */
> -void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
> +void uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
>  {
> -	struct uprobe *uprobe;
> -
> -	uprobe = find_uprobe(inode, offset);
> -	if (WARN_ON(!uprobe))
> -		return;
> -
> +	get_uprobe(uprobe);
>  	down_write(&uprobe->register_rwsem);
>  	__uprobe_unregister(uprobe, uc);
>  	up_write(&uprobe->register_rwsem);
> @@ -1120,7 +1114,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume
>  }
>  EXPORT_SYMBOL_GPL(uprobe_unregister);
>  
> -/*
> +/**
>   * uprobe_register - register a probe
>   * @inode: the file in which the probe has to be placed.
>   * @offset: offset from the start of the file.
> @@ -1136,40 +1130,40 @@ EXPORT_SYMBOL_GPL(uprobe_unregister);
>   * unregisters. Caller of uprobe_register() is required to keep @inode
>   * (and the containing mount) referenced.
>   *
> - * Return errno if it cannot successully install probes
> - * else return 0 (success)
> + * Return: pointer to the new uprobe on success or an ERR_PTR on failure.
>   */
> -int uprobe_register(struct inode *inode, loff_t offset, loff_t ref_ctr_offset,
> -		    struct uprobe_consumer *uc)
> +struct uprobe *uprobe_register(struct inode *inode,
> +				loff_t offset, loff_t ref_ctr_offset,
> +				struct uprobe_consumer *uc)
>  {
>  	struct uprobe *uprobe;
>  	int ret;
>  
>  	/* Uprobe must have at least one set consumer */
>  	if (!uc->handler && !uc->ret_handler)
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
>  
>  	/* copy_insn() uses read_mapping_page() or shmem_read_mapping_page() */
>  	if (!inode->i_mapping->a_ops->read_folio &&
>  	    !shmem_mapping(inode->i_mapping))
> -		return -EIO;
> +		return ERR_PTR(-EIO);
>  	/* Racy, just to catch the obvious mistakes */
>  	if (offset > i_size_read(inode))
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
>  
>  	/*
>  	 * This ensures that copy_from_page(), copy_to_page() and
>  	 * __update_ref_ctr() can't cross page boundary.
>  	 */
>  	if (!IS_ALIGNED(offset, UPROBE_SWBP_INSN_SIZE))
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
>  	if (!IS_ALIGNED(ref_ctr_offset, sizeof(short)))
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
>  
>   retry:
>  	uprobe = alloc_uprobe(inode, offset, ref_ctr_offset);
>  	if (IS_ERR(uprobe))
> -		return PTR_ERR(uprobe);
> +		return uprobe;
>  
>  	/*
>  	 * We can race with uprobe_unregister()->delete_uprobe().
> @@ -1188,35 +1182,29 @@ int uprobe_register(struct inode *inode, loff_t offset, loff_t ref_ctr_offset,
>  
>  	if (unlikely(ret == -EAGAIN))
>  		goto retry;
> -	return ret;
> +
> +	return ret ? ERR_PTR(ret) : uprobe;
>  }
>  EXPORT_SYMBOL_GPL(uprobe_register);
>  
> -/*
> - * uprobe_apply - unregister an already registered probe.
> - * @inode: the file in which the probe has to be removed.
> - * @offset: offset from the start of the file.
> +/**
> + * uprobe_apply - add or remove the breakpoints according to @uc->filter
> + * @uprobe: uprobe which "owns" the breakpoint
>   * @uc: consumer which wants to add more or remove some breakpoints
>   * @add: add or remove the breakpoints
> + * Return: 0 on success or negative error code.
>   */
> -int uprobe_apply(struct inode *inode, loff_t offset,
> -			struct uprobe_consumer *uc, bool add)
> +int uprobe_apply(struct uprobe *uprobe, struct uprobe_consumer *uc, bool add)
>  {
> -	struct uprobe *uprobe;
>  	struct uprobe_consumer *con;
>  	int ret = -ENOENT;
>  
> -	uprobe = find_uprobe(inode, offset);
> -	if (WARN_ON(!uprobe))
> -		return ret;
> -
>  	down_write(&uprobe->register_rwsem);
>  	for (con = uprobe->consumers; con && con != uc ; con = con->next)
>  		;
>  	if (con)
>  		ret = register_for_each_vma(uprobe, add ? uc : NULL);
>  	up_write(&uprobe->register_rwsem);
> -	put_uprobe(uprobe);
>  
>  	return ret;
>  }
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index afa909e17824..4e391daafa64 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -3160,6 +3160,7 @@ struct bpf_uprobe {
>  	loff_t offset;
>  	unsigned long ref_ctr_offset;
>  	u64 cookie;
> +	struct uprobe *uprobe;
>  	struct uprobe_consumer consumer;
>  };
>  
> @@ -3178,15 +3179,12 @@ struct bpf_uprobe_multi_run_ctx {
>  	struct bpf_uprobe *uprobe;
>  };
>  
> -static void bpf_uprobe_unregister(struct path *path, struct bpf_uprobe *uprobes,
> -				  u32 cnt)
> +static void bpf_uprobe_unregister(struct bpf_uprobe *uprobes, u32 cnt)
>  {
>  	u32 i;
>  
> -	for (i = 0; i < cnt; i++) {
> -		uprobe_unregister(d_real_inode(path->dentry), uprobes[i].offset,
> -				  &uprobes[i].consumer);
> -	}
> +	for (i = 0; i < cnt; i++)
> +		uprobe_unregister(uprobes[i].uprobe, &uprobes[i].consumer);
>  }
>  
>  static void bpf_uprobe_multi_link_release(struct bpf_link *link)
> @@ -3194,7 +3192,7 @@ static void bpf_uprobe_multi_link_release(struct bpf_link *link)
>  	struct bpf_uprobe_multi_link *umulti_link;
>  
>  	umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
> -	bpf_uprobe_unregister(&umulti_link->path, umulti_link->uprobes, umulti_link->cnt);
> +	bpf_uprobe_unregister(umulti_link->uprobes, umulti_link->cnt);
>  	if (umulti_link->task)
>  		put_task_struct(umulti_link->task);
>  	path_put(&umulti_link->path);
> @@ -3480,12 +3478,13 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
>  		      &bpf_uprobe_multi_link_lops, prog);
>  
>  	for (i = 0; i < cnt; i++) {
> -		err = uprobe_register(d_real_inode(link->path.dentry),
> -				      uprobes[i].offset,
> -				      uprobes[i].ref_ctr_offset,
> -				      &uprobes[i].consumer);
> -		if (err) {
> -			bpf_uprobe_unregister(&path, uprobes, i);
> +		uprobes[i].uprobe = uprobe_register(d_real_inode(link->path.dentry),
> +						    uprobes[i].offset,
> +						    uprobes[i].ref_ctr_offset,
> +						    &uprobes[i].consumer);
> +		if (IS_ERR(uprobes[i].uprobe)) {
> +			err = PTR_ERR(uprobes[i].uprobe);
> +			bpf_uprobe_unregister(uprobes, i);
>  			goto error_free;
>  		}
>  	}
> diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
> index 1f590f989c1e..52e76a73fa7c 100644
> --- a/kernel/trace/trace_uprobe.c
> +++ b/kernel/trace/trace_uprobe.c
> @@ -58,8 +58,8 @@ struct trace_uprobe {
>  	struct dyn_event		devent;
>  	struct uprobe_consumer		consumer;
>  	struct path			path;
> -	struct inode			*inode;
>  	char				*filename;
> +	struct uprobe			*uprobe;
>  	unsigned long			offset;
>  	unsigned long			ref_ctr_offset;
>  	unsigned long			nhit;
> @@ -1084,16 +1084,16 @@ typedef bool (*filter_func_t)(struct uprobe_consumer *self,
>  
>  static int trace_uprobe_enable(struct trace_uprobe *tu, filter_func_t filter)
>  {
> -	int ret;
> +	struct inode *inode = d_real_inode(tu->path.dentry);
> +	struct uprobe *uprobe;
>  
>  	tu->consumer.filter = filter;
> -	tu->inode = d_real_inode(tu->path.dentry);
> -
> -	ret = uprobe_register(tu->inode, tu->offset, tu->ref_ctr_offset, &tu->consumer);
> -	if (ret)
> -		tu->inode = NULL;
> +	uprobe = uprobe_register(inode, tu->offset, tu->ref_ctr_offset, &tu->consumer);
> +	if (IS_ERR(uprobe))
> +		return PTR_ERR(uprobe);
>  
> -	return ret;
> +	tu->uprobe = uprobe;
> +	return 0;
>  }
>  
>  static void __probe_event_disable(struct trace_probe *tp)
> @@ -1104,11 +1104,11 @@ static void __probe_event_disable(struct trace_probe *tp)
>  	WARN_ON(!uprobe_filter_is_empty(tu->tp.event->filter));
>  
>  	list_for_each_entry(tu, trace_probe_probe_list(tp), tp.list) {
> -		if (!tu->inode)
> +		if (!tu->uprobe)
>  			continue;
>  
> -		uprobe_unregister(tu->inode, tu->offset, &tu->consumer);
> -		tu->inode = NULL;
> +		uprobe_unregister(tu->uprobe, &tu->consumer);
> +		tu->uprobe = NULL;
>  	}
>  }
>  
> @@ -1305,7 +1305,7 @@ static int uprobe_perf_close(struct trace_event_call *call,
>  		return 0;
>  
>  	list_for_each_entry(tu, trace_probe_probe_list(tp), tp.list) {
> -		ret = uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
> +		ret = uprobe_apply(tu->uprobe, &tu->consumer, false);
>  		if (ret)
>  			break;
>  	}
> @@ -1329,7 +1329,7 @@ static int uprobe_perf_open(struct trace_event_call *call,
>  		return 0;
>  
>  	list_for_each_entry(tu, trace_probe_probe_list(tp), tp.list) {
> -		err = uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
> +		err = uprobe_apply(tu->uprobe, &tu->consumer, true);
>  		if (err) {
>  			uprobe_perf_close(call, event);
>  			break;
> -- 
> 2.25.1.362.g51ebf55
> 
> 


-- 
Masami Hiramatsu (Google) <mhiramat@...nel.org>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ