Re: [PATCH] drivers: pci: Fix flexible array usage

From: Purva Yeshi
Date: Thu Feb 13 2025 - 05:49:15 EST


On 12/02/25 02:48, Keith Busch wrote:
On Tue, Feb 11, 2025 at 03:02:35PM -0600, Bjorn Helgaas wrote:
This is kind of a complicated data structure. IIUC, a struct
pci_saved_state is allocated only in pci_store_saved_state(), where
the size is determined by the sum of the sizes of all the entries in
the dev->saved_cap_space list.

The pci_saved_state is filled by copying from entries in the
dev->saved_cap_space list. The entries need not be all the same size
because we copy each entry manually based on its size.

So cap[] is really just the base of this buffer of variable-sized
entries. Maybe "struct pci_cap_saved_data cap[]" is not the best
representation of this, but *cap (a pointer) doesn't seem better.

The original code is actually correct despite using a flexible array of
a struct that contains a flexible array. That arrangement just means you
can't index into it, but the code is only doing pointer arithmetic, so
should be fine.

With this struct:

struct pci_saved_state {
u32 config_space[16];
struct pci_cap_saved_data cap[];
};

Accessing "cap" field returns the address right after the config_space[]
member. When it's changed to a pointer type, though, it needs to be
initialized to *something* but the code doesn't do that. The code just
expects the cap to follow right after the config.

Anyway, to silence the warning we can just make it an anonymous member
and add 1 to the state to get to the same place in memory as before.

---
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 869d204a70a37..e562037644fd0 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1929,7 +1929,6 @@ EXPORT_SYMBOL(pci_restore_state);
struct pci_saved_state {
u32 config_space[16];
- struct pci_cap_saved_data cap[];
};
/**
@@ -1961,7 +1960,7 @@ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev)
memcpy(state->config_space, dev->saved_config_space,
sizeof(state->config_space));
- cap = state->cap;
+ cap = (void *)(state + 1);
hlist_for_each_entry(tmp, &dev->saved_cap_space, next) {
size_t len = sizeof(struct pci_cap_saved_data) + tmp->cap.size;
memcpy(cap, &tmp->cap, len);
@@ -1991,7 +1990,7 @@ int pci_load_saved_state(struct pci_dev *dev,
memcpy(dev->saved_config_space, state->config_space,
sizeof(state->config_space));
- cap = state->cap;
+ cap = (void *)(state + 1);
while (cap->size) {
struct pci_cap_saved_state *tmp;
--

Thanks for the clarification. I now see that the original use of a flexible array inside 'pci_saved_state' is correct. Would you like me to resend the patch with the modifications you suggested?