2022-02-28 12:03:39

by Jakob Koschel

[permalink] [raw]
Subject: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

If the list does not contain the expected element, the value of
list_for_each_entry() iterator will not point to a valid structure.
To avoid type confusion in such case, the list iterator
scope will be limited to list_for_each_entry() loop.

In preparation to limiting scope of a list iterator to the list traversal
loop, use a dedicated pointer to point to the found element.
Determining if an element was found is then simply checking if
the pointer is != NULL.

Signed-off-by: Jakob Koschel <[email protected]>
---
arch/x86/kernel/cpu/sgx/encl.c | 6 +++--
drivers/scsi/scsi_transport_sas.c | 17 ++++++++-----
drivers/thermal/thermal_core.c | 38 ++++++++++++++++++----------
drivers/usb/gadget/configfs.c | 22 ++++++++++------
drivers/usb/gadget/udc/max3420_udc.c | 11 +++++---
drivers/usb/gadget/udc/tegra-xudc.c | 11 +++++---
drivers/usb/mtu3/mtu3_gadget.c | 11 +++++---
drivers/usb/musb/musb_gadget.c | 11 +++++---
drivers/vfio/mdev/mdev_core.c | 11 +++++---
9 files changed, 88 insertions(+), 50 deletions(-)

diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
index 48afe96ae0f0..6c916416decc 100644
--- a/arch/x86/kernel/cpu/sgx/encl.c
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -450,7 +450,8 @@ static void sgx_mmu_notifier_release(struct mmu_notifier *mn,
struct mm_struct *mm)
{
struct sgx_encl_mm *encl_mm = container_of(mn, struct sgx_encl_mm, mmu_notifier);
- struct sgx_encl_mm *tmp = NULL;
+ struct sgx_encl_mm *found_encl_mm = NULL;
+ struct sgx_encl_mm *tmp;

/*
* The enclave itself can remove encl_mm. Note, objects can't be moved
@@ -460,12 +461,13 @@ static void sgx_mmu_notifier_release(struct mmu_notifier *mn,
list_for_each_entry(tmp, &encl_mm->encl->mm_list, list) {
if (tmp == encl_mm) {
list_del_rcu(&encl_mm->list);
+ found_encl_mm = tmp;
break;
}
}
spin_unlock(&encl_mm->encl->mm_lock);

- if (tmp == encl_mm) {
+ if (found_encl_mm) {
synchronize_srcu(&encl_mm->encl->srcu);
mmu_notifier_put(mn);
}
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 4ee578b181da..a8cbd90db9d2 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -1060,26 +1060,29 @@ EXPORT_SYMBOL(sas_port_get_phy);
* connected to a remote device is a port, so ports must be formed on
* all devices with phys if they're connected to anything.
*/
-void sas_port_add_phy(struct sas_port *port, struct sas_phy *phy)
+void sas_port_add_phy(struct sas_port *port, struct sas_phy *_phy)
{
mutex_lock(&port->phy_list_mutex);
- if (unlikely(!list_empty(&phy->port_siblings))) {
+ if (unlikely(!list_empty(&_phy->port_siblings))) {
/* make sure we're already on this port */
+ struct sas_phy *phy = NULL;
struct sas_phy *tmp;

list_for_each_entry(tmp, &port->phy_list, port_siblings)
- if (tmp == phy)
+ if (tmp == _phy) {
+ phy = tmp;
break;
+ }
/* If this trips, you added a phy that was already
* part of a different port */
- if (unlikely(tmp != phy)) {
+ if (unlikely(!phy)) {
dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n",
- dev_name(&phy->dev));
+ dev_name(&_phy->dev));
BUG();
}
} else {
- sas_port_create_link(port, phy);
- list_add_tail(&phy->port_siblings, &port->phy_list);
+ sas_port_create_link(port, _phy);
+ list_add_tail(&_phy->port_siblings, &port->phy_list);
port->num_phys++;
}
mutex_unlock(&port->phy_list_mutex);
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 82654dc8382b..97198543448b 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -625,24 +625,30 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
{
struct thermal_instance *dev;
struct thermal_instance *pos;
- struct thermal_zone_device *pos1;
- struct thermal_cooling_device *pos2;
+ struct thermal_zone_device *pos1 = NULL;
+ struct thermal_zone_device *tmp1;
+ struct thermal_cooling_device *pos2 = NULL;
+ struct thermal_cooling_device *tmp2;
unsigned long max_state;
int result, ret;

if (trip >= tz->trips || trip < 0)
return -EINVAL;

- list_for_each_entry(pos1, &thermal_tz_list, node) {
- if (pos1 == tz)
+ list_for_each_entry(tmp1, &thermal_tz_list, node) {
+ if (tmp1 == tz) {
+ pos1 = tmp1;
break;
+ }
}
- list_for_each_entry(pos2, &thermal_cdev_list, node) {
- if (pos2 == cdev)
+ list_for_each_entry(tmp2, &thermal_cdev_list, node) {
+ if (tmp2 == cdev) {
+ pos2 = tmp2;
break;
+ }
}

- if (tz != pos1 || cdev != pos2)
+ if (!pos1 || !pos2)
return -EINVAL;

ret = cdev->ops->get_max_state(cdev, &max_state);
@@ -1074,15 +1080,18 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
const struct thermal_zone_params *tzp;
struct thermal_zone_device *tz;
struct thermal_cooling_device *pos = NULL;
+ struct thermal_cooling_device *tmp;

if (!cdev)
return;

mutex_lock(&thermal_list_lock);
- list_for_each_entry(pos, &thermal_cdev_list, node)
- if (pos == cdev)
+ list_for_each_entry(tmp, &thermal_cdev_list, node)
+ if (tmp == cdev) {
+ pos = tmp;
break;
- if (pos != cdev) {
+ }
+ if (!pos) {
/* thermal cooling device not found */
mutex_unlock(&thermal_list_lock);
return;
@@ -1335,6 +1344,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
const struct thermal_zone_params *tzp;
struct thermal_cooling_device *cdev;
struct thermal_zone_device *pos = NULL;
+ struct thermal_zone_device *tmp;

if (!tz)
return;
@@ -1343,10 +1353,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
tz_id = tz->id;

mutex_lock(&thermal_list_lock);
- list_for_each_entry(pos, &thermal_tz_list, node)
- if (pos == tz)
+ list_for_each_entry(tmp, &thermal_tz_list, node)
+ if (tmp == tz) {
+ pos = tmp;
break;
- if (pos != tz) {
+ }
+ if (!pos) {
/* thermal zone device not found */
mutex_unlock(&thermal_list_lock);
return;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index d4a678c0806e..99f10cbd8878 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -418,7 +418,8 @@ static int config_usb_cfg_link(

struct usb_function_instance *fi =
to_usb_function_instance(usb_func_ci);
- struct usb_function_instance *a_fi;
+ struct usb_function_instance *a_fi = NULL;
+ struct usb_function_instance *tmp;
struct usb_function *f;
int ret;

@@ -428,11 +429,13 @@ static int config_usb_cfg_link(
* from another gadget or a random directory.
* Also a function instance can only be linked once.
*/
- list_for_each_entry(a_fi, &gi->available_func, cfs_list) {
- if (a_fi == fi)
+ list_for_each_entry(tmp, &gi->available_func, cfs_list) {
+ if (tmp == fi) {
+ a_fi = tmp;
break;
+ }
}
- if (a_fi != fi) {
+ if (!a_fi) {
ret = -EINVAL;
goto out;
}
@@ -882,15 +885,18 @@ static int os_desc_link(struct config_item *os_desc_ci,
struct gadget_info *gi = os_desc_item_to_gadget_info(os_desc_ci);
struct usb_composite_dev *cdev = &gi->cdev;
struct config_usb_cfg *c_target = to_config_usb_cfg(usb_cfg_ci);
- struct usb_configuration *c;
+ struct usb_configuration *c = NULL;
+ struct usb_configuration *tmp;
int ret;

mutex_lock(&gi->lock);
- list_for_each_entry(c, &cdev->configs, list) {
- if (c == &c_target->c)
+ list_for_each_entry(tmp, &cdev->configs, list) {
+ if (tmp == &c_target->c) {
+ c = tmp;
break;
+ }
}
- if (c != &c_target->c) {
+ if (!c) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
index d2a2b20cc1ad..d1b010b5f4a0 100644
--- a/drivers/usb/gadget/udc/max3420_udc.c
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -1044,22 +1044,25 @@ static int max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req,

static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
- struct max3420_req *t, *req = to_max3420_req(_req);
+ struct max3420_req *t = NULL;
+ struct max3420_req *req = to_max3420_req(_req);
+ struct max3420_req *tmp;
struct max3420_ep *ep = to_max3420_ep(_ep);
unsigned long flags;

spin_lock_irqsave(&ep->lock, flags);

/* Pluck the descriptor from queue */
- list_for_each_entry(t, &ep->queue, queue)
- if (t == req) {
+ list_for_each_entry(tmp, &ep->queue, queue)
+ if (tmp == req) {
list_del_init(&req->queue);
+ t = tmp;
break;
}

spin_unlock_irqrestore(&ep->lock, flags);

- if (t == req)
+ if (t)
max3420_req_done(req, -ECONNRESET);

return 0;
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 43f1b0d461c1..c37e3148a208 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -1413,18 +1413,21 @@ __tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
struct tegra_xudc_request *req)
{
struct tegra_xudc *xudc = ep->xudc;
- struct tegra_xudc_request *r;
+ struct tegra_xudc_request *r = NULL;
+ struct tegra_xudc_request *tmp;
struct tegra_xudc_trb *deq_trb;
bool busy, kick_queue = false;
int ret = 0;

/* Make sure the request is actually queued to this endpoint. */
- list_for_each_entry(r, &ep->queue, list) {
- if (r == req)
+ list_for_each_entry(tmp, &ep->queue, list) {
+ if (tmp == req) {
+ r = tmp;
break;
+ }
}

- if (r != req)
+ if (!r)
return -EINVAL;

/* Request hasn't been queued in the transfer ring yet. */
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index 9977600616d7..2e4daaa081a0 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -323,7 +323,8 @@ static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)
{
struct mtu3_ep *mep = to_mtu3_ep(ep);
struct mtu3_request *mreq = to_mtu3_request(req);
- struct mtu3_request *r;
+ struct mtu3_request *r = NULL;
+ struct mtu3_request *tmp;
struct mtu3 *mtu = mep->mtu;
unsigned long flags;
int ret = 0;
@@ -336,11 +337,13 @@ static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)

spin_lock_irqsave(&mtu->lock, flags);

- list_for_each_entry(r, &mep->req_list, list) {
- if (r == mreq)
+ list_for_each_entry(tmp, &mep->req_list, list) {
+ if (tmp == mreq) {
+ r = tmp;
break;
+ }
}
- if (r != mreq) {
+ if (!r) {
dev_dbg(mtu->dev, "req=%p not queued to %s\n", req, ep->name);
ret = -EINVAL;
goto done;
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 51274b87f46c..26b61ad7ab1b 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1266,7 +1266,8 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request)
{
struct musb_ep *musb_ep = to_musb_ep(ep);
struct musb_request *req = to_musb_request(request);
- struct musb_request *r;
+ struct musb_request *r = NULL;
+ struct musb_request *tmp;
unsigned long flags;
int status = 0;
struct musb *musb = musb_ep->musb;
@@ -1278,11 +1279,13 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request)

spin_lock_irqsave(&musb->lock, flags);

- list_for_each_entry(r, &musb_ep->req_list, list) {
- if (r == req)
+ list_for_each_entry(tmp, &musb_ep->req_list, list) {
+ if (tmp == req) {
+ r = tmp;
break;
+ }
}
- if (r != req) {
+ if (!r) {
dev_err(musb->controller, "request %p not queued to %s\n",
request, ep->name);
status = -EINVAL;
diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c
index b314101237fe..52cfa44c24a7 100644
--- a/drivers/vfio/mdev/mdev_core.c
+++ b/drivers/vfio/mdev/mdev_core.c
@@ -337,16 +337,19 @@ int mdev_device_create(struct mdev_type *type, const guid_t *uuid)

int mdev_device_remove(struct mdev_device *mdev)
{
- struct mdev_device *tmp;
+ struct mdev_device *tmp = NULL;
+ struct mdev_device *iter;
struct mdev_parent *parent = mdev->type->parent;

mutex_lock(&mdev_list_lock);
- list_for_each_entry(tmp, &mdev_list, next) {
- if (tmp == mdev)
+ list_for_each_entry(iter, &mdev_list, next) {
+ if (iter == mdev) {
+ tmp = iter;
break;
+ }
}

- if (tmp != mdev) {
+ if (!tmp) {
mutex_unlock(&mdev_list_lock);
return -ENODEV;
}
--
2.25.1


2022-02-28 12:23:47

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 12:08:18PM +0100, Jakob Koschel wrote:
> If the list does not contain the expected element, the value of
> list_for_each_entry() iterator will not point to a valid structure.
> To avoid type confusion in such case, the list iterator
> scope will be limited to list_for_each_entry() loop.
>
> In preparation to limiting scope of a list iterator to the list traversal
> loop, use a dedicated pointer to point to the found element.
> Determining if an element was found is then simply checking if
> the pointer is != NULL.
>
> Signed-off-by: Jakob Koschel <[email protected]>
> ---
> arch/x86/kernel/cpu/sgx/encl.c | 6 +++--
> drivers/scsi/scsi_transport_sas.c | 17 ++++++++-----
> drivers/thermal/thermal_core.c | 38 ++++++++++++++++++----------
> drivers/usb/gadget/configfs.c | 22 ++++++++++------
> drivers/usb/gadget/udc/max3420_udc.c | 11 +++++---
> drivers/usb/gadget/udc/tegra-xudc.c | 11 +++++---
> drivers/usb/mtu3/mtu3_gadget.c | 11 +++++---
> drivers/usb/musb/musb_gadget.c | 11 +++++---
> drivers/vfio/mdev/mdev_core.c | 11 +++++---
> 9 files changed, 88 insertions(+), 50 deletions(-)

The drivers/usb/ portion of this patch should be in patch 1/X, right?

Also, you will have to split these up per-subsystem so that the
different subsystem maintainers can take these in their trees.

thanks,

greg k-h

2022-02-28 13:24:01

by Christian König

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

Am 28.02.22 um 12:08 schrieb Jakob Koschel:
> If the list does not contain the expected element, the value of
> list_for_each_entry() iterator will not point to a valid structure.
> To avoid type confusion in such case, the list iterator
> scope will be limited to list_for_each_entry() loop.

We explicitly have the list_entry_is_head() macro to test after a loop
if the element pointer points to the head of the list instead of a valid
list entry.

So at least from my side I absolutely don't think that this is a good idea.

> In preparation to limiting scope of a list iterator to the list traversal
> loop, use a dedicated pointer to point to the found element.
> Determining if an element was found is then simply checking if
> the pointer is != NULL.

Since when do we actually want to do this?

Take this code here as an example:
> diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
> index 48afe96ae0f0..6c916416decc 100644
> --- a/arch/x86/kernel/cpu/sgx/encl.c
> +++ b/arch/x86/kernel/cpu/sgx/encl.c
> @@ -450,7 +450,8 @@ static void sgx_mmu_notifier_release(struct mmu_notifier *mn,
> struct mm_struct *mm)
> {
> struct sgx_encl_mm *encl_mm = container_of(mn, struct sgx_encl_mm, mmu_notifier);
> - struct sgx_encl_mm *tmp = NULL;
> + struct sgx_encl_mm *found_encl_mm = NULL;
> + struct sgx_encl_mm *tmp;
>
> /*
> * The enclave itself can remove encl_mm. Note, objects can't be moved
> @@ -460,12 +461,13 @@ static void sgx_mmu_notifier_release(struct mmu_notifier *mn,
> list_for_each_entry(tmp, &encl_mm->encl->mm_list, list) {
> if (tmp == encl_mm) {
> list_del_rcu(&encl_mm->list);
> + found_encl_mm = tmp;
> break;
> }
> }
> spin_unlock(&encl_mm->encl->mm_lock);
>
> - if (tmp == encl_mm) {
> + if (found_encl_mm) {
> synchronize_srcu(&encl_mm->encl->srcu);
> mmu_notifier_put(mn);
> }

I don't think that using the extra variable makes the code in any way
more reliable or easier to read.

Regards,
Christian.

2022-02-28 20:15:55

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 12:10 PM Linus Torvalds
<[email protected]> wrote:
>
> We can do
>
> typeof(pos) pos
>
> in the 'for ()' loop, and never use __iter at all.
>
> That means that inside the for-loop, we use a _different_ 'pos' than outside.

The thing that makes me throw up in my mouth a bit is that in that

typeof(pos) pos

the first 'pos' (that we use for just the typeof) is that outer-level
'pos', IOW it's a *different* 'pos' than the second 'pos' in that same
declaration that declares the inner level shadowing new 'pos'
variable.

If I was a compiler person, I would say "Linus, that thing is too ugly
to live", and I would hate it. I'm just hoping that even compiler
people say "that's *so* ugly it's almost beautiful".

Because it does seem to work. It's not pretty, but hey, it's not like
our headers are really ever be winning any beauty contests...

Linus

2022-02-28 20:21:10

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 12:03 PM Linus Torvalds
<[email protected]> wrote:
>
> Side note: we do need *some* way to do it.

Ooh.

This patch is a work of art.

And I mean that in the worst possible way.

We can do

typeof(pos) pos

in the 'for ()' loop, and never use __iter at all.

That means that inside the for-loop, we use a _different_ 'pos' than outside.

And then the compiler will not see some "might be uninitialized", but
the outer 'pos' *will* be uninitialized.

Unless, of course, the outer 'pos' had that pointless explicit initializer.

Here - can somebody poke holes in this "work of art" patch?

Linus


Attachments:
patch.diff (1.88 kB)

2022-02-28 20:37:24

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 4:19 AM Christian König
<[email protected]> wrote:
>
> I don't think that using the extra variable makes the code in any way
> more reliable or easier to read.

So I think the next step is to do the attached patch (which requires
that "-std=gnu11" that was discussed in the original thread).

That will guarantee that the 'pos' parameter of list_for_each_entry()
is only updated INSIDE the for_each_list_entry() loop, and can never
point to the (wrongly typed) head entry.

And I would actually hope that it should actually cause compiler
warnings about possibly uninitialized variables if people then use the
'pos' pointer outside the loop. Except

(a) that code in sgx/encl.c currently initializes 'tmp' to NULL for
inexplicable reasons - possibly because it already expected this
behavior

(b) when I remove that NULL initializer, I still don't get a warning,
because we've disabled -Wno-maybe-uninitialized since it results in so
many false positives.

Oh well.

Anyway, give this patch a look, and at least if it's expanded to do
"(pos) = NULL" in the entry statement for the for-loop, it will avoid
the HEAD type confusion that Jakob is working on. And I think in a
cleaner way than the horrid games he plays.

(But it won't avoid possible CPU speculation of such type confusion.
That, in my opinion, is a completely different issue)

I do wish we could actually poison the 'pos' value after the loop
somehow - but clearly the "might be uninitialized" I was hoping for
isn't the way to do it.

Anybody have any ideas?

Linus


Attachments:
p (881.00 B)

2022-02-28 21:22:02

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, 2022-02-28 at 21:56 +0100, Christian König wrote:
>
> Am 28.02.22 um 21:42 schrieb James Bottomley:
> > On Mon, 2022-02-28 at 21:07 +0100, Christian König wrote:
> > > Am 28.02.22 um 20:56 schrieb Linus Torvalds:
> > > > On Mon, Feb 28, 2022 at 4:19 AM Christian König
> > > > <[email protected]> wrote:
> > > > [SNIP]
> > > > Anybody have any ideas?
> > > I think we should look at the use cases why code is touching
> > > (pos)
> > > after the loop.
> > >
> > > Just from skimming over the patches to change this and experience
> > > with the drivers/subsystems I help to maintain I think the
> > > primary pattern looks something like this:
> > >
> > > list_for_each_entry(entry, head, member) {
> > > if (some_condition_checking(entry))
> > > break;
> > > }
> > > do_something_with(entry);
> >
> > Actually, we usually have a check to see if the loop found
> > anything, but in that case it should something like
> >
> > if (list_entry_is_head(entry, head, member)) {
> > return with error;
> > }
> > do_somethin_with(entry);
> >
> > Suffice? The list_entry_is_head() macro is designed to cope with
> > the bogus entry on head problem.
>
> That will work and is also what people already do.
>
> The key problem is that we let people do the same thing over and
> over again with slightly different implementations.
>
> Out in the wild I've seen at least using a separate variable, using
> a bool to indicate that something was found and just assuming that
> the list has an entry.
>
> The last case is bogus and basically what can break badly.

Yes, I understand that. I'm saying we should replace that bogus checks
of entry->something against some_value loop termination condition with
the list_entry_is_head() macro. That should be a one line and fairly
mechanical change rather than the explosion of code changes we seem to
have in the patch series.

James


2022-02-28 22:16:08

by Jakob Koschel

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr



> On 28. Feb 2022, at 21:56, Christian König <[email protected]> wrote:
>
>
>
> Am 28.02.22 um 21:42 schrieb James Bottomley:
>> On Mon, 2022-02-28 at 21:07 +0100, Christian König wrote:
>>> Am 28.02.22 um 20:56 schrieb Linus Torvalds:
>>>> On Mon, Feb 28, 2022 at 4:19 AM Christian König
>>>> <[email protected]> wrote:
>>>> [SNIP]
>>>> Anybody have any ideas?
>>> I think we should look at the use cases why code is touching (pos)
>>> after the loop.
>>>
>>> Just from skimming over the patches to change this and experience
>>> with the drivers/subsystems I help to maintain I think the primary
>>> pattern looks something like this:
>>>
>>> list_for_each_entry(entry, head, member) {
>>> if (some_condition_checking(entry))
>>> break;
>>> }
>>> do_something_with(entry);

There are other cases where the list iterator variable is used after the loop
Some examples:

- list_for_each_entry_continue() and list_for_each_entry_from().

- (although very rare) the head is actually of the correct struct type.
(ppc440spe_get_group_entry(): drivers/dma/ppc4xx/adma.c:1436)

- to use pos->list for example for list_add_tail():
(add_static_vm_early(): arch/arm/mm/ioremap.c:107)

If the scope of the list iterator is limited those still need fixing in a different way.

>>
>> Actually, we usually have a check to see if the loop found anything,
>> but in that case it should something like
>>
>> if (list_entry_is_head(entry, head, member)) {
>> return with error;
>> }
>> do_somethin_with(entry);
>>
>> Suffice? The list_entry_is_head() macro is designed to cope with the
>> bogus entry on head problem.
>
> That will work and is also what people already do.
>
> The key problem is that we let people do the same thing over and over again with slightly different implementations.
>
> Out in the wild I've seen at least using a separate variable, using a bool to indicate that something was found and just assuming that the list has an entry.
>
> The last case is bogus and basically what can break badly.
>
> If we would have an unified macro which search for an entry combined with automated reporting on patches to use that macro I think the potential to introduce such issues will already go down massively without auditing tons of existing code.

Having a unified way to do the same thing would indeed be great.

>
> Regards,
> Christian.
>
>>
>> James
>>
>>
>

- Jakob

2022-02-28 22:22:47

by Mike Rapoport

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr



On February 28, 2022 10:42:53 PM GMT+02:00, James Bottomley <[email protected]> wrote:
>On Mon, 2022-02-28 at 21:07 +0100, Christian König wrote:
>> Am 28.02.22 um 20:56 schrieb Linus Torvalds:
>> > On Mon, Feb 28, 2022 at 4:19 AM Christian König
>> > <[email protected]> wrote:
>> > > I don't think that using the extra variable makes the code in any
>> > > way
>> > > more reliable or easier to read.
>> > So I think the next step is to do the attached patch (which
>> > requires
>> > that "-std=gnu11" that was discussed in the original thread).
>> >
>> > That will guarantee that the 'pos' parameter of
>> > list_for_each_entry()
>> > is only updated INSIDE the for_each_list_entry() loop, and can
>> > never
>> > point to the (wrongly typed) head entry.
>> >
>> > And I would actually hope that it should actually cause compiler
>> > warnings about possibly uninitialized variables if people then use
>> > the
>> > 'pos' pointer outside the loop. Except
>> >
>> > (a) that code in sgx/encl.c currently initializes 'tmp' to NULL
>> > for
>> > inexplicable reasons - possibly because it already expected this
>> > behavior
>> >
>> > (b) when I remove that NULL initializer, I still don't get a
>> > warning,
>> > because we've disabled -Wno-maybe-uninitialized since it results in
>> > so
>> > many false positives.
>> >
>> > Oh well.
>> >
>> > Anyway, give this patch a look, and at least if it's expanded to do
>> > "(pos) = NULL" in the entry statement for the for-loop, it will
>> > avoid the HEAD type confusion that Jakob is working on. And I think
>> > in a cleaner way than the horrid games he plays.
>> >
>> > (But it won't avoid possible CPU speculation of such type
>> > confusion. That, in my opinion, is a completely different issue)
>>
>> Yes, completely agree.
>>
>> > I do wish we could actually poison the 'pos' value after the loop
>> > somehow - but clearly the "might be uninitialized" I was hoping for
>> > isn't the way to do it.
>> >
>> > Anybody have any ideas?
>>
>> I think we should look at the use cases why code is touching (pos)
>> after the loop.
>>
>> Just from skimming over the patches to change this and experience
>> with the drivers/subsystems I help to maintain I think the primary
>> pattern looks something like this:
>>
>> list_for_each_entry(entry, head, member) {
>> if (some_condition_checking(entry))
>> break;
>> }
>> do_something_with(entry);
>
>
>Actually, we usually have a check to see if the loop found anything,
>but in that case it should something like
>
>if (list_entry_is_head(entry, head, member)) {
> return with error;
>}
>do_somethin_with(entry);
>
>Suffice? The list_entry_is_head() macro is designed to cope with the
>bogus entry on head problem.

Won't suffice because the end goal of this work is to limit scope of entry only to loop. Hence the need for additional variable.

Besides, there are no guarantees that people won't do_something_with(entry) without the check or won't compare entry to NULL to check if the loop finished with break or not.

>James


--
Sincerely yours,
Mike

2022-02-28 22:53:55

by Barnabás Pőcze

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

Hi


2022. február 28., hétfő 23:28 keltezéssel, James Bottomley írta:
> [...]
> Well, yes, but my objection is more to the size of churn than the
> desire to do loop local. I'm not even sure loop local is possible,
> because it's always annoyed me that for (int i = 0; ... in C++ defines
> i in the outer scope not the loop scope, which is why I never use it.

It is arguably off-topic to the discussion at hand, but I think you might be
thinking of something else (or maybe it was the case in an ancient version of C++)
because that does not appear to be case. If it were,

for (int i ...) { ... }
for (int i ...) { ... }

would have to trigger a redeclaration error, but that happens neither in C++ nor in C.
The variable is also inaccessible outside the loop.


> [...]


Regards,
Barnabás Pőcze

2022-02-28 23:44:43

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 12:37:15PM -0800, Linus Torvalds wrote:
> On Mon, Feb 28, 2022 at 12:16 PM Matthew Wilcox <[email protected]> wrote:
> >
> > Then we can never use -Wshadow ;-( I'd love to be able to turn it on;
> > it catches real bugs.
>
> Oh, we already can never use -Wshadow regardless of things like this.
> That bridge hasn't just been burned, it never existed in the first
> place.
>
> The whole '-Wshadow' thing simply cannot work with local variables in
> macros - something that we've used since day 1.
>
> Try this (as a "p.c" file):
>
> #define min(a,b) ({ \
> typeof(a) __a = (a); \
> typeof(b) __b = (b); \
> __a < __b ? __a : __b; })
>
> int min3(int a, int b, int c)
> {
> return min(a,min(b,c));
> }
>
> and now do "gcc -O2 -S t.c".
>
> Then try it with -Wshadow.

#define ___PASTE(a, b) a##b
#define __PASTE(a, b) ___PASTE(a, b)
#define _min(a, b, u) ({ \
typeof(a) __PASTE(__a,u) = (a); \
typeof(b) __PASTE(__b,u) = (b); \
__PASTE(__a,u) < __PASTE(__b,u) ? __PASTE(__a,u) : __PASTE(__b,u); })

#define min(a, b) _min(a, b, __COUNTER__)

int min3(int a, int b, int c)
{
return min(a,min(b,c));
}

(probably there's a more elegant way to do this)

2022-03-01 01:16:39

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 1:47 PM Jakob Koschel <[email protected]> wrote:
>
> The goal of this is to get compiler warnings right? This would indeed be great.

Yes, so I don't mind having a one-time patch that has been gathered
using some automated checker tool, but I don't think that works from a
long-term maintenance perspective.

So if we have the basic rule being "don't use the loop iterator after
the loop has finished, because it can cause all kinds of subtle
issues", then in _addition_ to fixing the existing code paths that
have this issue, I really would want to (a) get a compiler warning for
future cases and (b) make it not actually _work_ for future cases.

Because otherwise it will just happen again.

> Changing the list_for_each_entry() macro first will break all of those cases
> (e.g. the ones using 'list_entry_is_head()).

So I have no problems with breaking cases that we basically already
have a patch for due to your automated tool. There were certainly
more than a handful, but it didn't look _too_ bad to just make the
rule be "don't use the iterator after the loop".

Of course, that's just based on that patch of yours. Maybe there are a
ton of other cases that your patch didn't change, because they didn't
match your trigger case, so I may just be overly optimistic here.

But basically to _me_, the important part is that the end result is
maintainable longer-term. I'm more than happy to have a one-time patch
to fix a lot of dubious cases if we can then have clean rules going
forward.

> I assumed it is better to fix those cases first and then have a simple
> coccinelle script changing the macro + moving the iterator into the scope
> of the macro.

So that had been another plan of mine, until I actually looked at
changing the macro. In the one case I looked at, it was ugly beyond
belief.

It turns out that just syntactically, it's really nice to give the
type of the iterator from outside the way we do now. Yeah, it may be a
bit odd, and maybe it's partly because I'm so used to the
"list_for_each_list_entry()" syntax, but moving the type into the loop
construct really made it nasty - either one very complex line, or
having to split it over two lines which was even worse.

Maybe the place I looked at just happened to have a long typename, but
it's basically always going to be a struct, so it's never a _simple_
type. And it just looked very odd adn unnatural to have the type as
one of the "arguments" to that list_for_each_entry() macro.

So yes, initially my idea had been to just move the iterator entirely
inside the macro. But specifying the type got so ugly that I think
that

typeof (pos) pos

trick inside the macro really ends up giving us the best of all worlds:

(a) let's us keep the existing syntax and code for all the nice cases
that did everything inside the loop anyway

(b) gives us a nice warning for any normal use-after-loop case
(unless you explicitly initialized it like that
sgx_mmu_notifier_release() function did for no good reason

(c) also guarantees that even if you don't get a warning,
non-converted (or newly written) bad code won't actually _work_

so you end up getting the new rules without any ambiguity or mistaken

> With this you are no longer able to set the 'outer' pos within the list
> iterator loop body or am I missing something?

Correct. Any assignment inside the loop will be entirely just to the
local loop case. So any "break;" out of the loop will have to set
another variable - like your updated patch did.

> I fail to see how this will make most of the changes in this
> patch obsolete (if that was the intention).

I hope my explanation above clarifies my thinking: I do not dislike
your patch, and in fact your patch is indeed required to make the new
semantics work.

What I disliked was always the maintainability of your patch - making
the rules be something that isn't actually visible in the source code,
and letting the old semantics still work as well as they ever did, and
having to basically run some verification pass to find bad users.

(I also disliked your original patch that mixed up the "CPU
speculation type safety" with the actual non-speculative problems, but
that was another issue).

Linus

2022-03-01 07:06:16

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, 28 Feb 2022 16:41:04 -0800 Linus Torvalds wrote:
> So yes, initially my idea had been to just move the iterator entirely
> inside the macro. But specifying the type got so ugly that I think
> that
>
> typeof (pos) pos
>
> trick inside the macro really ends up giving us the best of all worlds:
>
> (a) let's us keep the existing syntax and code for all the nice cases
> that did everything inside the loop anyway
>
> (b) gives us a nice warning for any normal use-after-loop case
> (unless you explicitly initialized it like that
> sgx_mmu_notifier_release() function did for no good reason
>
> (c) also guarantees that even if you don't get a warning,
> non-converted (or newly written) bad code won't actually _work_
>
> so you end up getting the new rules without any ambiguity or mistaken

I presume the goal is that we can do this without changing existing
code? Otherwise actually moving the iterator into the loop body would
be an option, by creating a different hidden variable:

#define list_iter(head) \
for (struct list head *_l = (head)->next; _l != (head); _l = _l->next)

#define list_iter_entry(var, member) \
list_entry(_l, typeof(*var), member)


list_iter(&p->a_head) {
struct entry *e = list_iter_entry(e, a_member);

/* use e->... */
}


Or we can slide into soft insanity and exploit one of Kees'es tricks
to encode the type of the entries "next to" the head:

#define LIST_HEAD_MEM(name, type) \
union { \
struct list_head name; \
type *name ## _entry; \
}

struct entry {
struct list_head a_member;
};

struct parent {
LIST_HEAD_MEM(a_head, struct entry);
};

#define list_for_each_magic(pos, head, member) \
for (typeof(**(head ## _entry)) *pos = list_first_entry(head, typeof(**(head ## _entry)), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))


list_for_each_magic(e, &p->a_head, a_member) {
/* use e->... */
}


I'll show myself out...

2022-03-01 14:13:36

by Jakob Koschel

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr



> On 1. Mar 2022, at 01:41, Linus Torvalds <[email protected]> wrote:
>
> On Mon, Feb 28, 2022 at 1:47 PM Jakob Koschel <[email protected]> wrote:
>>
>> The goal of this is to get compiler warnings right? This would indeed be great.
>
> Yes, so I don't mind having a one-time patch that has been gathered
> using some automated checker tool, but I don't think that works from a
> long-term maintenance perspective.
>
> So if we have the basic rule being "don't use the loop iterator after
> the loop has finished, because it can cause all kinds of subtle
> issues", then in _addition_ to fixing the existing code paths that
> have this issue, I really would want to (a) get a compiler warning for
> future cases and (b) make it not actually _work_ for future cases.
>
> Because otherwise it will just happen again.
>
>> Changing the list_for_each_entry() macro first will break all of those cases
>> (e.g. the ones using 'list_entry_is_head()).
>
> So I have no problems with breaking cases that we basically already
> have a patch for due to your automated tool. There were certainly
> more than a handful, but it didn't look _too_ bad to just make the
> rule be "don't use the iterator after the loop".
>
> Of course, that's just based on that patch of yours. Maybe there are a
> ton of other cases that your patch didn't change, because they didn't
> match your trigger case, so I may just be overly optimistic here.

Based on the coccinelle script there are ~480 cases that need fixing
in total. I'll now finish all of them and then split them by
submodules as Greg suggested and repost a patch set per submodule.
Sounds good?

>
> But basically to _me_, the important part is that the end result is
> maintainable longer-term. I'm more than happy to have a one-time patch
> to fix a lot of dubious cases if we can then have clean rules going
> forward.
>
>> I assumed it is better to fix those cases first and then have a simple
>> coccinelle script changing the macro + moving the iterator into the scope
>> of the macro.
>
> So that had been another plan of mine, until I actually looked at
> changing the macro. In the one case I looked at, it was ugly beyond
> belief.
>
> It turns out that just syntactically, it's really nice to give the
> type of the iterator from outside the way we do now. Yeah, it may be a
> bit odd, and maybe it's partly because I'm so used to the
> "list_for_each_list_entry()" syntax, but moving the type into the loop
> construct really made it nasty - either one very complex line, or
> having to split it over two lines which was even worse.
>
> Maybe the place I looked at just happened to have a long typename, but
> it's basically always going to be a struct, so it's never a _simple_
> type. And it just looked very odd adn unnatural to have the type as
> one of the "arguments" to that list_for_each_entry() macro.
>
> So yes, initially my idea had been to just move the iterator entirely
> inside the macro. But specifying the type got so ugly that I think
> that
>
> typeof (pos) pos
>
> trick inside the macro really ends up giving us the best of all worlds:
>
> (a) let's us keep the existing syntax and code for all the nice cases
> that did everything inside the loop anyway
>
> (b) gives us a nice warning for any normal use-after-loop case
> (unless you explicitly initialized it like that
> sgx_mmu_notifier_release() function did for no good reason
>
> (c) also guarantees that even if you don't get a warning,
> non-converted (or newly written) bad code won't actually _work_
>
> so you end up getting the new rules without any ambiguity or mistaken
>
>> With this you are no longer able to set the 'outer' pos within the list
>> iterator loop body or am I missing something?
>
> Correct. Any assignment inside the loop will be entirely just to the
> local loop case. So any "break;" out of the loop will have to set
> another variable - like your updated patch did.
>
>> I fail to see how this will make most of the changes in this
>> patch obsolete (if that was the intention).
>
> I hope my explanation above clarifies my thinking: I do not dislike
> your patch, and in fact your patch is indeed required to make the new
> semantics work.

ok it's all clear now, thanks for clarifying.
I've defined all the 'tmp' iterator variables uninitialized so applying
your patch on top of that later will just give the nice compiler warning
if they are used past the loop body.

>
> What I disliked was always the maintainability of your patch - making
> the rules be something that isn't actually visible in the source code,
> and letting the old semantics still work as well as they ever did, and
> having to basically run some verification pass to find bad users.

Since this patch is not a complete list of cases that need fixing (30%)
I haven't included the actual change of moving the iterator variable
into the loop and thought that would be a second step coming after this
is merged.

With these changes alone, yes you still rely on manual verification passes.

>
> (I also disliked your original patch that mixed up the "CPU
> speculation type safety" with the actual non-speculative problems, but
> that was another issue).
>
> Linus

- Jakob

2022-03-01 19:21:17

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Tue, Mar 1, 2022 at 10:14 AM Kees Cook <[email protected]> wrote:
>
> The first big glitch with -Wshadow was with shadowed global variables.
> GCC 4.8 fixed that, but it still yells about shadowed functions. What
> _almost_ works is -Wshadow=local.

Heh. Yeah, I just have long memories of "-Wshadow was a disaster". You
looked into the details.

> Another way to try to catch misused shadow variables is
> -Wunused-but-set-varible, but it, too, has tons of false positives.

That on the face of it should be an easy warning to get technically
right for a compiler.

So I assume the "false positives" are simply because we end up having
various variables that really don't end up being used - and
"intentionally" so).

Or rather, they might only be used under some config option - perhaps
the use is even syntactically there and parsed, but the compiler
notices that it's turned off under some

if (IS_ENABLED(..))

option? Because yeah, we have a lot of those.

I think that's a common theme with a lot of compiler warnings: on the
face of it they sound "obviously sane" and nobody should ever write
code like that.

A conditional that is always true? Sounds idiotic, and sounds like a
reasonable thing for a compiler to warn about, since why would you
have a conditional in the first place for that?

But then you realize that maybe the conditional is a build config
option, and "always true" suddenly makes sense. Or it's a test for
something that is always true on _that_architecture_ but not in some
general sense (ie testing "sizeof()"). Or it's a purely syntactic
conditional, like "do { } while (0)".

It's why I'm often so down on a lot of the odd warnings that are
hiding under W=1 and friends. They all may make sense in the trivial
case ("That is insane") but then in the end they happen for sane code.

And yeah, -Wshadow has had tons of history with macro nesting, and
just being badly done in the first place (eg "strlen" can be a
perfectly fine local variable).

That said, maybe people could ask the gcc and clan people for a way to
_mark_ the places where we expect to validly see shadowing. For
example, that "local variable in a macro expression statement" thing
is absolutely horrendous to fix with preprocessor tricks to try to
make for unique identifiers.

But I think it would be much more syntactically reasonable to add (for
example) a "shadow" attribute to such a variable exactly to tell the
compiler "yeah, yeah, I know this identifier could shadow an outer
one" and turn it off that way.

Linus

2022-03-01 19:49:19

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 2:29 PM James Bottomley
<[email protected]> wrote:
>
> However, if the desire is really to poison the loop variable then we
> can do
>
> #define list_for_each_entry(pos, head, member) \
> for (pos = list_first_entry(head, typeof(*pos), member); \
> !list_entry_is_head(pos, head, member) && ((pos = NULL) == NULL; \
> pos = list_next_entry(pos, member))
>
> Which would at least set pos to NULL when the loop completes.

That would actually have been excellent if we had done that
originally. It would not only avoid the stale and incorrectly typed
head entry left-over turd, it would also have made it very easy to
test for "did I find an entry in the loop".

But I don't much like it in the situation we are now.

Why? Mainly because it basically changes the semantics of the loop
_without_ any warnings about it. And we don't actually get the
advantage of the nicer semantics, because we can't actually make code
do

list_for_each_entry(entry, ....) {
..
}
if (!entry)
return -ESRCH;
.. use the entry we found ..

because that would be a disaster for back-porting, plus it would be a
flag-day issue (ie we'd have to change the semantics of the loop at
the same time we change every single user).

So instead of that simple "if (!entry)", we'd effectively have to
continue to use something that still works with the old world order
(ie that "if (list_entry_is_head())" model).

So we couldn't really take _advantage_ of the nicer semantics, and
we'd not even get a warning if somebody does it wrong - the code would
just silently do the wrong thing.

IOW: I don't think you are wrong about that patch: it would solve the
problem that Jakob wants to solve, and it would have absolutely been
much better if we had done this from the beginning. But I think that
in our current situation, it's actually a really fragile solution to
the "don't do that then" problem we have.

Linus

2022-03-01 20:24:36

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Tue, Mar 01, 2022 at 10:14:07AM -0800, Kees Cook wrote:
> On Mon, Feb 28, 2022 at 04:45:11PM -0800, Linus Torvalds wrote:
> > Really. The "-Wshadow doesn't work on the kernel" is not some new
> > issue, because you have to do completely insane things to the source
> > code to enable it.
>
> The first big glitch with -Wshadow was with shadowed global variables.
> GCC 4.8 fixed that, but it still yells about shadowed functions. What
> _almost_ works is -Wshadow=local. At first glace, all the warnings
> look solvable, but then one will eventually discover __wait_event()
> and associated macros that mix when and how deeply it intentionally
> shadows variables. :)

Well, that's just disgusting. Macros fundamentally shouldn't be
referring to things that aren't in their arguments. The first step to
cleaning this up is ...

I'll take a look at the rest of cleaning this up soon.

From 28ffe35d56223d4242b915832299e5acc926737e Mon Sep 17 00:00:00 2001
From: "Matthew Wilcox (Oracle)" <[email protected]>
Date: Tue, 1 Mar 2022 13:47:07 -0500
Subject: [PATCH] wait: Parameterize the return variable to ___wait_event()

Macros should not refer to variables which aren't in their arguments.
Pass the name from its callers.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
include/linux/swait.h | 12 ++++++------
include/linux/wait.h | 32 ++++++++++++++++----------------
include/linux/wait_bit.h | 4 ++--
3 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/include/linux/swait.h b/include/linux/swait.h
index 6a8c22b8c2a5..5e8e9b13be2d 100644
--- a/include/linux/swait.h
+++ b/include/linux/swait.h
@@ -191,14 +191,14 @@ do { \
} while (0)

#define __swait_event_timeout(wq, condition, timeout) \
- ___swait_event(wq, ___wait_cond_timeout(condition), \
+ ___swait_event(wq, ___wait_cond_timeout(condition, __ret), \
TASK_UNINTERRUPTIBLE, timeout, \
__ret = schedule_timeout(__ret))

#define swait_event_timeout_exclusive(wq, condition, timeout) \
({ \
long __ret = timeout; \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __swait_event_timeout(wq, condition, timeout); \
__ret; \
})
@@ -216,14 +216,14 @@ do { \
})

#define __swait_event_interruptible_timeout(wq, condition, timeout) \
- ___swait_event(wq, ___wait_cond_timeout(condition), \
+ ___swait_event(wq, ___wait_cond_timeout(condition, __ret), \
TASK_INTERRUPTIBLE, timeout, \
__ret = schedule_timeout(__ret))

#define swait_event_interruptible_timeout_exclusive(wq, condition, timeout)\
({ \
long __ret = timeout; \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __swait_event_interruptible_timeout(wq, \
condition, timeout); \
__ret; \
@@ -252,7 +252,7 @@ do { \
} while (0)

#define __swait_event_idle_timeout(wq, condition, timeout) \
- ___swait_event(wq, ___wait_cond_timeout(condition), \
+ ___swait_event(wq, ___wait_cond_timeout(condition, __ret), \
TASK_IDLE, timeout, \
__ret = schedule_timeout(__ret))

@@ -278,7 +278,7 @@ do { \
#define swait_event_idle_timeout_exclusive(wq, condition, timeout) \
({ \
long __ret = timeout; \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __swait_event_idle_timeout(wq, \
condition, timeout); \
__ret; \
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 851e07da2583..890cce3c0f2e 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -271,7 +271,7 @@ static inline void wake_up_pollfree(struct wait_queue_head *wq_head)
__wake_up_pollfree(wq_head);
}

-#define ___wait_cond_timeout(condition) \
+#define ___wait_cond_timeout(condition, __ret) \
({ \
bool __cond = (condition); \
if (__cond && !__ret) \
@@ -386,7 +386,7 @@ do { \
})

#define __wait_event_timeout(wq_head, condition, timeout) \
- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition, __ret), \
TASK_UNINTERRUPTIBLE, 0, timeout, \
__ret = schedule_timeout(__ret))

@@ -413,13 +413,13 @@ do { \
({ \
long __ret = timeout; \
might_sleep(); \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_timeout(wq_head, condition, timeout); \
__ret; \
})

#define __wait_event_freezable_timeout(wq_head, condition, timeout) \
- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition, __ret), \
TASK_INTERRUPTIBLE, 0, timeout, \
__ret = freezable_schedule_timeout(__ret))

@@ -431,7 +431,7 @@ do { \
({ \
long __ret = timeout; \
might_sleep(); \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_freezable_timeout(wq_head, condition, timeout); \
__ret; \
})
@@ -503,7 +503,7 @@ do { \
})

#define __wait_event_interruptible_timeout(wq_head, condition, timeout) \
- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition, __ret), \
TASK_INTERRUPTIBLE, 0, timeout, \
__ret = schedule_timeout(__ret))

@@ -531,7 +531,7 @@ do { \
({ \
long __ret = timeout; \
might_sleep(); \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_interruptible_timeout(wq_head, \
condition, timeout); \
__ret; \
@@ -698,7 +698,7 @@ do { \
} while (0)

#define __wait_event_idle_timeout(wq_head, condition, timeout) \
- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition, __ret), \
TASK_IDLE, 0, timeout, \
__ret = schedule_timeout(__ret))

@@ -725,13 +725,13 @@ do { \
({ \
long __ret = timeout; \
might_sleep(); \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_idle_timeout(wq_head, condition, timeout); \
__ret; \
})

#define __wait_event_idle_exclusive_timeout(wq_head, condition, timeout) \
- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition, __ret), \
TASK_IDLE, 1, timeout, \
__ret = schedule_timeout(__ret))

@@ -762,7 +762,7 @@ do { \
({ \
long __ret = timeout; \
might_sleep(); \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_idle_exclusive_timeout(wq_head, condition, timeout);\
__ret; \
})
@@ -932,7 +932,7 @@ extern int do_wait_intr_irq(wait_queue_head_t *, wait_queue_entry_t *);
})

#define __wait_event_killable_timeout(wq_head, condition, timeout) \
- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition, __ret), \
TASK_KILLABLE, 0, timeout, \
__ret = schedule_timeout(__ret))

@@ -962,7 +962,7 @@ extern int do_wait_intr_irq(wait_queue_head_t *, wait_queue_entry_t *);
({ \
long __ret = timeout; \
might_sleep(); \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_killable_timeout(wq_head, \
condition, timeout); \
__ret; \
@@ -1107,7 +1107,7 @@ do { \
})

#define __wait_event_lock_irq_timeout(wq_head, condition, lock, timeout, state) \
- ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition, __ret), \
state, 0, timeout, \
spin_unlock_irq(&lock); \
__ret = schedule_timeout(__ret); \
@@ -1141,7 +1141,7 @@ do { \
timeout) \
({ \
long __ret = timeout; \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_lock_irq_timeout( \
wq_head, condition, lock, timeout, \
TASK_INTERRUPTIBLE); \
@@ -1151,7 +1151,7 @@ do { \
#define wait_event_lock_irq_timeout(wq_head, condition, lock, timeout) \
({ \
long __ret = timeout; \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_event_lock_irq_timeout( \
wq_head, condition, lock, timeout, \
TASK_UNINTERRUPTIBLE); \
diff --git a/include/linux/wait_bit.h b/include/linux/wait_bit.h
index 7dec36aecbd9..227e6a20a978 100644
--- a/include/linux/wait_bit.h
+++ b/include/linux/wait_bit.h
@@ -292,7 +292,7 @@ do { \
})

#define __wait_var_event_timeout(var, condition, timeout) \
- ___wait_var_event(var, ___wait_cond_timeout(condition), \
+ ___wait_var_event(var, ___wait_cond_timeout(condition, __ret), \
TASK_UNINTERRUPTIBLE, 0, timeout, \
__ret = schedule_timeout(__ret))

@@ -300,7 +300,7 @@ do { \
({ \
long __ret = timeout; \
might_sleep(); \
- if (!___wait_cond_timeout(condition)) \
+ if (!___wait_cond_timeout(condition, __ret)) \
__ret = __wait_var_event_timeout(var, condition, timeout); \
__ret; \
})
--
2.34.1

2022-03-01 21:33:25

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, Feb 28, 2022 at 04:45:11PM -0800, Linus Torvalds wrote:
> Really. The "-Wshadow doesn't work on the kernel" is not some new
> issue, because you have to do completely insane things to the source
> code to enable it.

The first big glitch with -Wshadow was with shadowed global variables.
GCC 4.8 fixed that, but it still yells about shadowed functions. What
_almost_ works is -Wshadow=local. At first glace, all the warnings
look solvable, but then one will eventually discover __wait_event()
and associated macros that mix when and how deeply it intentionally
shadows variables. :)

Another way to try to catch misused shadow variables is
-Wunused-but-set-varible, but it, too, has tons of false positives.

I tried to capture some of the rationale and research here:
https://github.com/KSPP/linux/issues/152

--
Kees Cook

2022-03-02 03:11:32

by David Laight

[permalink] [raw]
Subject: RE: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

From: Linus Torvalds
> Sent: 01 March 2022 19:07
> On Mon, Feb 28, 2022 at 2:29 PM James Bottomley
> <[email protected]> wrote:
> >
> > However, if the desire is really to poison the loop variable then we
> > can do
> >
> > #define list_for_each_entry(pos, head, member) \
> > for (pos = list_first_entry(head, typeof(*pos), member); \
> > !list_entry_is_head(pos, head, member) && ((pos = NULL) == NULL; \
> > pos = list_next_entry(pos, member))
> >
> > Which would at least set pos to NULL when the loop completes.
>
> That would actually have been excellent if we had done that
> originally. It would not only avoid the stale and incorrectly typed
> head entry left-over turd, it would also have made it very easy to
> test for "did I find an entry in the loop".
>
> But I don't much like it in the situation we are now.
>
> Why? Mainly because it basically changes the semantics of the loop
> _without_ any warnings about it. And we don't actually get the
> advantage of the nicer semantics, because we can't actually make code
> do
>
> list_for_each_entry(entry, ....) {
> ..
> }
> if (!entry)
> return -ESRCH;
> .. use the entry we found ..
>
> because that would be a disaster for back-porting, plus it would be a
> flag-day issue (ie we'd have to change the semantics of the loop at
> the same time we change every single user).
>
> So instead of that simple "if (!entry)", we'd effectively have to
> continue to use something that still works with the old world order
> (ie that "if (list_entry_is_head())" model).
>
> So we couldn't really take _advantage_ of the nicer semantics, and
> we'd not even get a warning if somebody does it wrong - the code would
> just silently do the wrong thing.
>
> IOW: I don't think you are wrong about that patch: it would solve the
> problem that Jakob wants to solve, and it would have absolutely been
> much better if we had done this from the beginning. But I think that
> in our current situation, it's actually a really fragile solution to
> the "don't do that then" problem we have.

Can it be resolved by making:
#define list_entry_is_head(pos, head, member) ((pos) == NULL)
and double-checking that it isn't used anywhere else (except in
the list macros themselves).

The odd ones I just found are fs/locks.c mm/page_reporting.c
security/apparmor/apparmorfs.c (3 times)

net/xfrm/xfrm_ipcomp.c#L244 is buggy.
(There is a WARN_ON() then it just carries on regardless!)

There are only about 25 uses of list_entry_is_head().

There are a lot more places where these lists seem to be scanned by hand.
I bet a few of those aren't actually right either.

(Oh at 3am this morning I thought it was a different list type
that could have much the same problem!)

Another plausible solution is a variant of list_foreach_entry()
that does set the 'entry' to NULL at the end.
Then code can be moved over in stages.
I'd reorder the arguments as well as changing the name!

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

2022-03-02 12:21:50

by Xiaomeng Tong

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Mon, 28 Feb 2022 16:41:04 -0800, Linus Torvalds
<[email protected]> wrote:
>
> But basically to _me_, the important part is that the end result is
> maintainable longer-term.

I couldn't agree more. And because of that, I stick with the following
approach because it's maintainable longer-term than "type(pos) pos" one:
Implements a new macro for each list_for_each_entry* with _inside suffix.
#define list_for_each_entry_inside(pos, type, head, member)

I have posted a patch series here to demonstrate this approach:
https://lore.kernel.org/lkml/[email protected]/

Although we need replace all the use of list_for_each_entry* (15000+)
with list_for_each_entry*_inside, the work can be done gradually rather
than all at once. We can incrementally replace these callers until
all these in the kernel are completely updated with *_inside* one. At
that time, we can just remove the implements of origin macros and rename
the *_inside* macro back to the origin name just in one single patch.

And the "type(pos) pos" approach need teach developers to "not initialize
the iterator variable, otherwise the use-after-loop will not be reported by
compiler", which is unreasonable and impossible for all developers.

And it will mess up the following code logic and no warnning reported by
compiler, even without initializing "ext" at the beginning:
void foo(struct mem_extent *arg) {
struct mem_extent *ext; // used both for iterator and normal ptr
...
ext = arg; // this assignment can alse be done in another bar() func
...
list_for_each_entry(ext, head, member) {
if (found(ext))
break;
}
...
// use ext after the loop
ret = ext;
}
If the loop hit the break, the last "ret" will be the found ext iterator.
However, if the "type(pos) pos" approach applied, the last "ret" will be
"arg" which is not the intention of the developers, because the "ext" is
two different variables inside and outside the loop.

Thus, my idea is *better a finger off than always aching*, let's choose
the "list_for_each_entry_inside(pos, type, head, member)" approach.

> It turns out that just syntactically, it's really nice to give the
> type of the iterator from outside the way we do now. Yeah, it may be a
> bit odd, and maybe it's partly because I'm so used to the
> "list_for_each_list_entry()" syntax, but moving the type into the loop
> construct really made it nasty - either one very complex line, or
> having to split it over two lines which was even worse.
>
> Maybe the place I looked at just happened to have a long typename, but
> it's basically always going to be a struct, so it's never a _simple_
> type. And it just looked very odd adn unnatural to have the type as
> one of the "arguments" to that list_for_each_entry() macro.

we can pass a shorter type name to list_for_each_entry_inside, thus no
need to split it over two lines. Actually it is not a big problem.
+ #define t struct sram_bank_info
- list_for_each_entry(pos, head, member) {
+ list_for_each_entry_inside(pos, t, head, member) {

I put the type at the second argument not the first to avoid messing up
the pattern match in some coccinelle scripts.

> (b) gives us a nice warning for any normal use-after-loop case
> (unless you explicitly initialized it like that
> sgx_mmu_notifier_release() function did for no good reason

sometimes developers can be confused by the reported warnning:
"used without having been initialized", and can not figure out immediately
that "oh, now i am using another different variable but with the same name
of the loop iterator variable", which has changed the programming habits
of developers.

> (c) also guarantees that even if you don't get a warning,
> non-converted (or newly written) bad code won't actually _work_
>
> so you end up getting the new rules without any ambiguity or mistaken

It will lead to a wrong/NULL pointer dereference if the pointer is used
anywhere else, depend on which value is used to initialized with.

Best regard,
--
Xiaomeng Tong

2022-03-03 08:42:48

by Xiaomeng Tong

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

> I think this would make sense, it would mean you only assign the containing
> element on valid elements.
>
> I was thinking something along the lines of:
>
> #define list_for_each_entry(pos, head, member) \
> for (struct list_head *list = head->next, typeof(pos) pos; \
> list == head ? 0 : (( pos = list_entry(pos, list, member), 1)); \
> list = list->next)
>
> Although the initialization block of the for loop is not valid C, I'm
> not sure there is any way to declare two variables of a different type
> in the initialization part of the loop.

It can be done using a *nested loop*, like this:

#define list_for_each_entry(pos, head, member) \
for (struct list_head *list = head->next, cond = (struct list_head *)-1; cond == (struct list_head *)-1; cond = NULL) \
for (typeof(pos) pos; \
list == head ? 0 : (( pos = list_entry(pos, list, member), 1)); \
list = list->next)

>
> I believe all this does is get rid of the &pos->member == (head) check
> to terminate the list.

Indeed, although the original way is harmless.

> It alone will not fix any of the other issues that using the iterator
> variable after the loop currently has.

Yes, but I stick with the list_for_each_entry_inside(pos, type, head, member)
way to make the iterator invisiable outside the loop (before and after the loop).
It is maintainable longer-term than "type(pos) pos" one and perfect.
see my explain:
https://lore.kernel.org/lkml/[email protected]/
and list_for_each_entry_inside(pos, type, head, member) patch here:
https://lore.kernel.org/lkml/[email protected]/

--
Xiaomeng Tong

2022-03-04 07:22:28

by Xiaomeng Tong

[permalink] [raw]
Subject: Re: [PATCH 2/6] treewide: remove using list iterator after loop body as a ptr

On Thu, 3 Mar 2022 12:18:24 +0000, Daniel Thompson wrote:
> On Thu, Mar 03, 2022 at 03:26:57PM +0800, Xiaomeng Tong wrote:
> > On Thu, 3 Mar 2022 04:58:23 +0000, David Laight wrote:
> > > on 3 Mar 2022 10:27:29 +0800, Xiaomeng Tong wrote:
> > > > The problem is the mis-use of iterator outside the loop on exit, and
> > > > the iterator will be the HEAD's container_of pointer which pointers
> > > > to a type-confused struct. Sidenote: The *mis-use* here refers to
> > > > mistakely access to other members of the struct, instead of the
> > > > list_head member which acutally is the valid HEAD.
> > >
> > > The problem is that the HEAD's container_of pointer should never
> > > be calculated at all.
> > > This is what is fundamentally broken about the current definition.
> >
> > Yes, the rule is "the HEAD's container_of pointer should never be
> > calculated at all outside the loop", but how do you make sure everyone
> > follows this rule?
>
> Your formulation of the rule is correct: never run container_of() on HEAD
> pointer.

Actually, it is not my rule. My rule is that never access other members
of the struct except for the list_head member after the loop, because
this is a invalid member after loop exit, but valid for the list_head
member which just is HEAD and the lately caculation (&pos->head) seems
harmless.

I have considered the case that the HEAD's container "pos" is layouted
across the max and the min address boundary, which means the address of
HEAD is likely 0x60, and the address of pos is likely 0xffffffe0.
It seems ok to caculate pos with:
((type *)(__mptr - offsetof(type, member)));
and it seems ok to caculate head outside the loop with:
if (&pos->head == &HEAD)
return NULL;

The only case I can think of with the rule "never run container_of()
on HEAD" must be followed is when the first argument (which is &HEAD)
passing to container_of() is NULL + some offset, it may lead to the
resulting "pos->member" access being a NULL dereference. But maybe
the caller can take the responsibility to check if it is NULL, not
container_of() itself.

Please remind me if i missed somthing, thanks.

>
> However the rule that is introduced by list_for_each_entry_inside() is
> *not* this rule. The rule it introduces is: never access the iterator
> variable outside the loop.

Sorry for the confusion, indeed, that is two *different* rule.

>
> Making the iterator NULL on loop exit does follow the rule you proposed
> but using a different technique: do not allow HEAD to be stored in the
> iterator variable after loop exit. This also makes it impossible to run
> container_of() on the HEAD pointer.
>

It does not. My rule is: never access the iterator variable outside the loop.
The "Making the iterator NULL on loop exit" way still leak the pos with NULL
outside the loop, may lead to a NULL deference.

>
> > Everyone makes mistakes, but we can eliminate them all from the beginning
> > with the help of compiler which can catch such use-after-loop things.
>
> Indeed but if we introduce new interfaces then we don't have to worry
> about existing usages and silent regressions. Code will have been
> written knowing the loop can exit with the iterator set to NULL.

Yes, it is more simple and compatible with existing interfaces. Howerver,
you should make every developers to remember that "pos will be set NULL on
loop exit", which is unreasonable and impossible for *every* single person.
Otherwise the mis-use-after-loop will lead to a NULL dereference.
But we can kill this problem by declaring iterator inside the loop and the
complier will catch it if somebody mis-use-after-loop.

>
> Sure it is still possible for programmers to make mistakes and
> dereference the NULL pointer but C programmers are well training w.r.t.
> NULL pointer checking so such mistakes are much less likely than with
> the current list_for_each_entry() macro. This risk must be offset
> against the way a NULLify approach can lead to more elegant code when we
> are doing a list search.
>

Yes, the NULLify approach is better than the current list_for_each_entry()
macro, but i stick with that the list_for_each_entry_inside() way is best
and perfect _technically_.

Thus, my idea is *better a finger off than always aching*, let's settle this
damn problem once and for all, with list_for_each_entry_inside().

--
Xiaomeng Tong