]>
Commit | Line | Data |
---|---|---|
b3216112 TL |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Manuel Ullmann <labre@posteo.de> | |
3 | Date: Mon, 18 Apr 2022 00:20:01 +0200 | |
4 | Subject: [PATCH] net: atlantic: invert deep par in pm functions, preventing | |
5 | null derefs | |
6 | ||
7 | commit cbe6c3a8f8f4315b96e46e1a1c70393c06d95a4c upstream. | |
8 | ||
9 | This will reset deeply on freeze and thaw instead of suspend and | |
10 | resume and prevent null pointer dereferences of the uninitialized ring | |
11 | 0 buffer while thawing. | |
12 | ||
13 | The impact is an indefinitely hanging kernel. You can't switch | |
14 | consoles after this and the only possible user interaction is SysRq. | |
15 | ||
16 | BUG: kernel NULL pointer dereference | |
17 | RIP: 0010:aq_ring_rx_fill+0xcf/0x210 [atlantic] | |
18 | aq_vec_init+0x85/0xe0 [atlantic] | |
19 | aq_nic_init+0xf7/0x1d0 [atlantic] | |
20 | atl_resume_common+0x4f/0x100 [atlantic] | |
21 | pci_pm_thaw+0x42/0xa0 | |
22 | ||
23 | resolves in aq_ring.o to | |
24 | ||
25 | ``` | |
26 | 0000000000000ae0 <aq_ring_rx_fill>: | |
27 | { | |
28 | /* ... */ | |
29 | baf: 48 8b 43 08 mov 0x8(%rbx),%rax | |
30 | buff->flags = 0U; /* buff is NULL */ | |
31 | ``` | |
32 | ||
33 | The bug has been present since the introduction of the new pm code in | |
34 | 8aaa112a57c1 ("net: atlantic: refactoring pm logic") and was hidden | |
35 | until 8ce84271697a ("net: atlantic: changes for multi-TC support"), | |
36 | which refactored the aq_vec_{free,alloc} functions into | |
37 | aq_vec_{,ring}_{free,alloc}, but is technically not wrong. The | |
38 | original functions just always reinitialized the buffers on S3/S4. If | |
39 | the interface is down before freezing, the bug does not occur. It does | |
40 | not matter, whether the initrd contains and loads the module before | |
41 | thawing. | |
42 | ||
43 | So the fix is to invert the boolean parameter deep in all pm function | |
44 | calls, which was clearly intended to be set like that. | |
45 | ||
46 | First report was on Github [1], which you have to guess from the | |
47 | resume logs in the posted dmesg snippet. Recently I posted one on | |
48 | Bugzilla [2], since I did not have an AQC device so far. | |
49 | ||
50 | #regzbot introduced: 8ce84271697a | |
51 | #regzbot from: koo5 <kolman.jindrich@gmail.com> | |
52 | #regzbot monitor: https://github.com/Aquantia/AQtion/issues/32 | |
53 | ||
54 | Fixes: 8aaa112a57c1 ("net: atlantic: refactoring pm logic") | |
55 | Link: https://github.com/Aquantia/AQtion/issues/32 [1] | |
56 | Link: https://bugzilla.kernel.org/show_bug.cgi?id=215798 [2] | |
57 | Cc: stable@vger.kernel.org | |
58 | Reported-by: koo5 <kolman.jindrich@gmail.com> | |
59 | Signed-off-by: Manuel Ullmann <labre@posteo.de> | |
60 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
61 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
62 | Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> | |
63 | --- | |
64 | .../ethernet/aquantia/atlantic/aq_pci_func.c | 20 +++++++++---------- | |
65 | 1 file changed, 10 insertions(+), 10 deletions(-) | |
66 | ||
67 | diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c | |
68 | index 797a95142d1f..3a529ee8c834 100644 | |
69 | --- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c | |
70 | +++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c | |
71 | @@ -443,25 +443,25 @@ static int atl_resume_common(struct device *dev, bool deep) | |
72 | } | |
73 | ||
74 | static int aq_pm_freeze(struct device *dev) | |
75 | -{ | |
76 | - return aq_suspend_common(dev, false); | |
77 | -} | |
78 | - | |
79 | -static int aq_pm_suspend_poweroff(struct device *dev) | |
80 | { | |
81 | return aq_suspend_common(dev, true); | |
82 | } | |
83 | ||
84 | +static int aq_pm_suspend_poweroff(struct device *dev) | |
85 | +{ | |
86 | + return aq_suspend_common(dev, false); | |
87 | +} | |
88 | + | |
89 | static int aq_pm_thaw(struct device *dev) | |
90 | -{ | |
91 | - return atl_resume_common(dev, false); | |
92 | -} | |
93 | - | |
94 | -static int aq_pm_resume_restore(struct device *dev) | |
95 | { | |
96 | return atl_resume_common(dev, true); | |
97 | } | |
98 | ||
99 | +static int aq_pm_resume_restore(struct device *dev) | |
100 | +{ | |
101 | + return atl_resume_common(dev, false); | |
102 | +} | |
103 | + | |
104 | static const struct dev_pm_ops aq_pm_ops = { | |
105 | .suspend = aq_pm_suspend_poweroff, | |
106 | .poweroff = aq_pm_suspend_poweroff, |