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] [day] [month] [year] [list]
Message-ID: <95685a8d1b0bc73e5d9b926c5c9de983a0802c70.camel@gmail.com>
Date: Wed, 22 Oct 2025 22:51:02 +0300
From: markus.suvanto@...il.com
To: David Howells <dhowells@...hat.com>
Cc: Marc Dionne <marc.dionne@...istor.com>, Christian Brauner
	 <christian@...uner.io>, linux-afs@...ts.infradead.org, 
	linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] afs: Fix dynamic lookup to fail on cell lookup failure

ke, 2025-10-22 kello 19:48 +0100, David Howells kirjoitti:
> When a process tries to access an entry in /afs, normally what happens is
> that an automount dentry is created by ->lookup() and then triggered, which
> jumps through the ->d_automount() op.  Currently, afs_dynroot_lookup() does
> not do cell DNS lookup, leaving that to afs_d_automount() to perform -
> however, it is possible to use access() or stat() on the automount point,
> which will always return successfully, have briefly created an afs_cell
> record if one did not already exist.
> 
> This means that something like:
> 
>         test -d "/afs/.west" && echo Directory exists
> 
> will print "Directory exists" even though no such cell is configured.  This
> breaks the "west" python module available on PIP as it expects this access
> to fail.
> 
> Now, it could be possible to make afs_dynroot_lookup() perform the DNS[*]
> lookup, but that would make "ls --color /afs" do this for each cell in /afs
> that is listed but not yet probed.  kafs-client, probably wrongly, preloads
> the entire cell database and all the known cells are then listed in /afs -
> and doing ls /afs would be very, very slow, especially if any cell supplied
> addresses but was wholly inaccessible.
> 
>  [*] When I say "DNS", actually read getaddrinfo(), which could use any one
>      of a host of mechanisms.  Could also use static configuration.
> 
> To fix this, make the following changes:
> 
>  (1) Create an enum to specify the origination point of a call to
>      afs_lookup_cell() and pass this value into that function in place of
>      the "excl" parameter (which can be derived from it).  There are six
>      points of origination:
> 
>         - Cell preload through /proc/net/afs/cells
>         - Root cell config through /proc/net/afs/rootcell
>         - Lookup in dynamic root
>         - Automount trigger
>         - Direct mount with mount() syscall
>         - Alias check where YFS tells us the cell name is different
> 
>  (2) Add an extra state into the afs_cell state machine to indicate a cell
>      that's been initialised, but not yet looked up.  This is separate from
>      one that can be considered active and has been looked up at least
>      once.
> 
>  (3) Make afs_lookup_cell() vary its behaviour more, depending on where it
>      was called from:
> 
>      If called from preload or root cell config, DNS lookup will not happen
>      until we definitely want to use the cell (dynroot mount, automount,
>      direct mount or alias check).  The cell will appear in /afs but stat()
>      won't trigger DNS lookup.
> 
>      If the cell already exists, dynroot will not wait for the DNS lookup
>      to complete.  If the cell did not already exist, dynroot will wait.
> 
>      If called from automount, direct mount or alias check, it will wait
>      for the DNS lookup to complete.
> 
>  (4) Make afs_lookup_cell() return an error if lookup failed in one way or
>      another.  We try to return -ENOENT if the DNS says the cell does not
>      exist and -EDESTADDRREQ if we couldn't access the DNS.
> 
> Reported-by: Markus Suvanto <markus.suvanto@...il.com>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220685
> Signed-off-by: David Howells <dhowells@...hat.com>
> cc: Marc Dionne <marc.dionne@...istor.com>
> cc: linux-afs@...ts.infradead.org
> ---
>  fs/afs/cell.c     |   78 +++++++++++++++++++++++++++++++++++++++++++++---------
>  fs/afs/dynroot.c  |    3 +-
>  fs/afs/internal.h |   12 +++++++-
>  fs/afs/mntpt.c    |    3 +-
>  fs/afs/proc.c     |    3 +-
>  fs/afs/super.c    |    2 -
>  fs/afs/vl_alias.c |    3 +-
>  7 files changed, 86 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/afs/cell.c b/fs/afs/cell.c
> index f31359922e98..d9b6fa1088b7 100644
> --- a/fs/afs/cell.c
> +++ b/fs/afs/cell.c
> @@ -229,7 +229,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
>   * @name:	The name of the cell.
>   * @namesz:	The strlen of the cell name.
>   * @vllist:	A colon/comma separated list of numeric IP addresses or NULL.
> - * @excl:	T if an error should be given if the cell name already exists.
> + * @reason:	The reason we're doing the lookup
>   * @trace:	The reason to be logged if the lookup is successful.
>   *
>   * Look up a cell record by name and query the DNS for VL server addresses if
> @@ -239,7 +239,8 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
>   */
>  struct afs_cell *afs_lookup_cell(struct afs_net *net,
>  				 const char *name, unsigned int namesz,
> -				 const char *vllist, bool excl,
> +				 const char *vllist,
> +				 enum afs_lookup_cell_for reason,
>  				 enum afs_cell_trace trace)
>  {
>  	struct afs_cell *cell, *candidate, *cursor;
> @@ -247,12 +248,18 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net,
>  	enum afs_cell_state state;
>  	int ret, n;
>  
> -	_enter("%s,%s", name, vllist);
> +	_enter("%s,%s,%u", name, vllist, reason);
>  
> -	if (!excl) {
> +	if (reason != AFS_LOOKUP_CELL_PRELOAD) {
>  		cell = afs_find_cell(net, name, namesz, trace);
> -		if (!IS_ERR(cell))
> +		if (!IS_ERR(cell)) {
> +			if (reason == AFS_LOOKUP_CELL_DYNROOT)
> +				goto no_wait;
> +			if (cell->state == AFS_CELL_SETTING_UP ||
> +			    cell->state == AFS_CELL_UNLOOKED)
> +				goto lookup_cell;
>  			goto wait_for_cell;
> +		}
>  	}
>  
>  	/* Assume we're probably going to create a cell and preallocate and
> @@ -298,26 +305,69 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net,
>  	rb_insert_color(&cell->net_node, &net->cells);
>  	up_write(&net->cells_lock);
>  
> -	afs_queue_cell(cell, afs_cell_trace_queue_new);
> +lookup_cell:
> +	if (reason != AFS_LOOKUP_CELL_PRELOAD &&
> +	    reason != AFS_LOOKUP_CELL_ROOTCELL) {
> +		set_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags);
> +		afs_queue_cell(cell, afs_cell_trace_queue_new);
> +	}
>  
>  wait_for_cell:
> -	_debug("wait_for_cell");
>  	state = smp_load_acquire(&cell->state); /* vs error */
> -	if (state != AFS_CELL_ACTIVE &&
> -	    state != AFS_CELL_DEAD) {
> +	switch (state) {
> +	case AFS_CELL_ACTIVE:
> +	case AFS_CELL_DEAD:
> +		break;
> +	case AFS_CELL_UNLOOKED:
> +	default:
> +		if (reason == AFS_LOOKUP_CELL_PRELOAD ||
> +		    reason == AFS_LOOKUP_CELL_ROOTCELL)
> +			break;
> +		_debug("wait_for_cell");
>  		afs_see_cell(cell, afs_cell_trace_wait);
>  		wait_var_event(&cell->state,
>  			       ({
>  				       state = smp_load_acquire(&cell->state); /* vs error */
>  				       state == AFS_CELL_ACTIVE || state == AFS_CELL_DEAD;
>  			       }));
> +		_debug("waited_for_cell %d %d", cell->state, cell->error);
>  	}
>  
> +no_wait:
>  	/* Check the state obtained from the wait check. */
> +	state = smp_load_acquire(&cell->state); /* vs error */
>  	if (state == AFS_CELL_DEAD) {
>  		ret = cell->error;
>  		goto error;
>  	}
> +	if (state == AFS_CELL_ACTIVE) {
> +		switch (cell->dns_status) {
> +		case DNS_LOOKUP_NOT_DONE:
> +			if (cell->dns_source == DNS_RECORD_FROM_CONFIG) {
> +				ret = 0;
> +				break;
> +			}
> +			fallthrough;
> +		default:
> +			ret = -EIO;
> +			goto error;
> +		case DNS_LOOKUP_GOOD:
> +		case DNS_LOOKUP_GOOD_WITH_BAD:
> +			ret = 0;
> +			break;
> +		case DNS_LOOKUP_GOT_NOT_FOUND:
> +			ret = -ENOENT;
> +			goto error;
> +		case DNS_LOOKUP_BAD:
> +			ret = -EREMOTEIO;
> +			goto error;
> +		case DNS_LOOKUP_GOT_LOCAL_FAILURE:
> +		case DNS_LOOKUP_GOT_TEMP_FAILURE:
> +		case DNS_LOOKUP_GOT_NS_FAILURE:
> +			ret = -EDESTADDRREQ;
> +			goto error;
> +		}
> +	}
>  
>  	_leave(" = %p [cell]", cell);
>  	return cell;
> @@ -325,7 +375,7 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net,
>  cell_already_exists:
>  	_debug("cell exists");
>  	cell = cursor;
> -	if (excl) {
> +	if (reason == AFS_LOOKUP_CELL_PRELOAD) {
>  		ret = -EEXIST;
>  	} else {
>  		afs_use_cell(cursor, trace);
> @@ -384,7 +434,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
>  		return -EINVAL;
>  
>  	/* allocate a cell record for the root/workstation cell */
> -	new_root = afs_lookup_cell(net, rootcell, len, vllist, false,
> +	new_root = afs_lookup_cell(net, rootcell, len, vllist,
> +				   AFS_LOOKUP_CELL_ROOTCELL,
>  				   afs_cell_trace_use_lookup_ws);
>  	if (IS_ERR(new_root)) {
>  		_leave(" = %ld", PTR_ERR(new_root));
> @@ -777,6 +828,7 @@ static bool afs_manage_cell(struct afs_cell *cell)
>  	switch (cell->state) {
>  	case AFS_CELL_SETTING_UP:
>  		goto set_up_cell;
> +	case AFS_CELL_UNLOOKED:
>  	case AFS_CELL_ACTIVE:
>  		goto cell_is_active;
>  	case AFS_CELL_REMOVING:
> @@ -797,7 +849,7 @@ static bool afs_manage_cell(struct afs_cell *cell)
>  		goto remove_cell;
>  	}
>  
> -	afs_set_cell_state(cell, AFS_CELL_ACTIVE);
> +	afs_set_cell_state(cell, AFS_CELL_UNLOOKED);
>  
>  cell_is_active:
>  	if (afs_has_cell_expired(cell, &next_manage))
> @@ -807,6 +859,8 @@ static bool afs_manage_cell(struct afs_cell *cell)
>  		ret = afs_update_cell(cell);
>  		if (ret < 0)
>  			cell->error = ret;
> +		if (cell->state == AFS_CELL_UNLOOKED)
> +			afs_set_cell_state(cell, AFS_CELL_ACTIVE);
>  	}
>  
>  	if (next_manage < TIME64_MAX && cell->net->live) {
> diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c
> index 8c6130789fde..dc9d29e3739e 100644
> --- a/fs/afs/dynroot.c
> +++ b/fs/afs/dynroot.c
> @@ -108,7 +108,8 @@ static struct dentry *afs_dynroot_lookup_cell(struct inode *dir, struct dentry *
>  		dotted = true;
>  	}
>  
> -	cell = afs_lookup_cell(net, name, len, NULL, false,
> +	cell = afs_lookup_cell(net, name, len, NULL,
> +			       AFS_LOOKUP_CELL_DYNROOT,
>  			       afs_cell_trace_use_lookup_dynroot);
>  	if (IS_ERR(cell)) {
>  		ret = PTR_ERR(cell);
> diff --git a/fs/afs/internal.h b/fs/afs/internal.h
> index bcbf828ba31f..a90b8ac56844 100644
> --- a/fs/afs/internal.h
> +++ b/fs/afs/internal.h
> @@ -344,6 +344,7 @@ extern const char afs_init_sysname[];
>  
>  enum afs_cell_state {
>  	AFS_CELL_SETTING_UP,
> +	AFS_CELL_UNLOOKED,
>  	AFS_CELL_ACTIVE,
>  	AFS_CELL_REMOVING,
>  	AFS_CELL_DEAD,
> @@ -1050,9 +1051,18 @@ static inline bool afs_cb_is_broken(unsigned int cb_break,
>  extern int afs_cell_init(struct afs_net *, const char *);
>  extern struct afs_cell *afs_find_cell(struct afs_net *, const char *, unsigned,
>  				      enum afs_cell_trace);
> +enum afs_lookup_cell_for {
> +	AFS_LOOKUP_CELL_DYNROOT,
> +	AFS_LOOKUP_CELL_MOUNTPOINT,
> +	AFS_LOOKUP_CELL_DIRECT_MOUNT,
> +	AFS_LOOKUP_CELL_PRELOAD,
> +	AFS_LOOKUP_CELL_ROOTCELL,
> +	AFS_LOOKUP_CELL_ALIAS_CHECK,
> +};
>  struct afs_cell *afs_lookup_cell(struct afs_net *net,
>  				 const char *name, unsigned int namesz,
> -				 const char *vllist, bool excl,
> +				 const char *vllist,
> +				 enum afs_lookup_cell_for reason,
>  				 enum afs_cell_trace trace);
>  extern struct afs_cell *afs_use_cell(struct afs_cell *, enum afs_cell_trace);
>  void afs_unuse_cell(struct afs_cell *cell, enum afs_cell_trace reason);
> diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
> index 1ad048e6e164..57c204a3c04e 100644
> --- a/fs/afs/mntpt.c
> +++ b/fs/afs/mntpt.c
> @@ -107,7 +107,8 @@ static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
>  		if (size > AFS_MAXCELLNAME)
>  			return -ENAMETOOLONG;
>  
> -		cell = afs_lookup_cell(ctx->net, p, size, NULL, false,
> +		cell = afs_lookup_cell(ctx->net, p, size, NULL,
> +				       AFS_LOOKUP_CELL_MOUNTPOINT,
>  				       afs_cell_trace_use_lookup_mntpt);
>  		if (IS_ERR(cell)) {
>  			pr_err("kAFS: unable to lookup cell '%pd'\n", mntpt);
> diff --git a/fs/afs/proc.c b/fs/afs/proc.c
> index 40e879c8ca77..44520549b509 100644
> --- a/fs/afs/proc.c
> +++ b/fs/afs/proc.c
> @@ -122,7 +122,8 @@ static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
>  	if (strcmp(buf, "add") == 0) {
>  		struct afs_cell *cell;
>  
> -		cell = afs_lookup_cell(net, name, strlen(name), args, true,
> +		cell = afs_lookup_cell(net, name, strlen(name), args,
> +				       AFS_LOOKUP_CELL_PRELOAD,
>  				       afs_cell_trace_use_lookup_add);
>  		if (IS_ERR(cell)) {
>  			ret = PTR_ERR(cell);
> diff --git a/fs/afs/super.c b/fs/afs/super.c
> index 9b1d8ac39261..354090b3a7e7 100644
> --- a/fs/afs/super.c
> +++ b/fs/afs/super.c
> @@ -305,7 +305,7 @@ static int afs_parse_source(struct fs_context *fc, struct fs_parameter *param)
>  	/* lookup the cell record */
>  	if (cellname) {
>  		cell = afs_lookup_cell(ctx->net, cellname, cellnamesz,
> -				       NULL, false,
> +				       NULL, AFS_LOOKUP_CELL_DIRECT_MOUNT,
>  				       afs_cell_trace_use_lookup_mount);
>  		if (IS_ERR(cell)) {
>  			pr_err("kAFS: unable to lookup cell '%*.*s'\n",
> diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
> index 709b4cdb723e..fc9676abd252 100644
> --- a/fs/afs/vl_alias.c
> +++ b/fs/afs/vl_alias.c
> @@ -269,7 +269,8 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
>  	if (!name_len || name_len > AFS_MAXCELLNAME)
>  		master = ERR_PTR(-EOPNOTSUPP);
>  	else
> -		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false,
> +		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL,
> +					 AFS_LOOKUP_CELL_ALIAS_CHECK,
>  					 afs_cell_trace_use_lookup_canonical);
>  	kfree(cell_name);
>  	if (IS_ERR(master))



Tested-by: Markus Suvanto <markus.suvanto@...il.com>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ