Thread (32 messages) 32 messages, 6 authors, 2021-08-26

Re: [PATCH net] net: dsa: sja1105: fix use-after-free after calling of_find_compatible_node, or worse

From: Saravana Kannan <hidden>
Date: 2021-08-20 01:26:24

On Thu, Aug 19, 2021 at 5:37 PM Vladimir Oltean [off-list ref] wrote:
On Thu, Aug 19, 2021 at 04:52:43PM -0700, Saravana Kannan wrote:
quoted
On Thu, Aug 19, 2021 at 6:35 AM Andrew Lunn [off-list ref] wrote:
quoted
quoted
(2) is what is happening in this case. fw_devlink=on sees that
"switch" implements the "switch_intc" and "switch" hasn't finished
probing yet. So it has no way of knowing that switch_intc is actually
ready. And even if switch_intc was registered as part of switch's
probe() by the time the PHYs are added, switch_intc could get
deregistered if the probe fails at a later point. So until probe()
returns 0, fw_devlink can't be fully sure the supplier (switch_intc)
is ready. Which is good in general because you won't have to
forcefully unbind (if that is even handled correctly in the first
place) the consumers of a device if it fails probe() half way through
registering a few services.
I had to read your email a couple of times before I understood it. I
think I do now, but apologies if I'm not making sense.
quoted
There are actually a few different circular references with the way
switches work. Take for example:

&fec1 {
        phy-mode = "rmii";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_fec1>;
        status = "okay";

        fixed-link {
                speed = <100>;
                full-duplex;
        };

        mdio1: mdio {
                #address-cells = <1>;
                #size-cells = <0>;
                clock-frequency = <12500000>;
                suppress-preamble;
                status = "okay";

                switch0: switch0@0 {
                        compatible = "marvell,mv88e6190";
                        pinctrl-0 = <&pinctrl_gpio_switch0>;
                        pinctrl-names = "default";
                        reg = <0>;
                        eeprom-length = <65536>;
                        interrupt-parent = <&gpio3>;
                        interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
                        interrupt-controller;
                        #interrupt-cells = <2>;

                        ports {
                                #address-cells = <1>;
                                #size-cells = <0>;

                                port@0 {
                                        reg = <0>;
                                        label = "cpu";
                                        ethernet = <&fec1>;

                                        fixed-link {
                                                speed = <100>;
                                                full-duplex;
                                        };
                                };

FEC is an ethernet controller. It has an MDIO bus, and on the bus is
an Ethernet switch. port 0 of the Ethernet switch is connected to the
FEC ethernet controller.

While the FEC probes, it will at some point register its MDIO bus. At
that point, the MDIO bus is probed, the switch is found, and
registered with the switch core. The switch core looks for the port
with an ethernet property and goes looking for that ethernet
interface. But that this point in time, the FEC probe has only got as
far as registering the MDIO bus. The interface itself is not
registered. So finding the interface fails, and we go into
EPROBE_DEFER for probing the switch.
Ok, I understood up to here. Couple of questions:
Is this EPROBE_DEFER causing an issue? Wouldn't the switch then
probe successfully when it's reattempted? And then things work
normally? I don't see what the problem is.
It's not an issue per se, since it's not a fully circular dependency:
the DSA master (the FEC controller) does not have any dependency on the
switch beneath it to probe (there's nothing like a phy-handle from the
FEC to the switch or to something provided by it).

A few EPROBE_DEFER iterations later the switch will finally find its DSA
master fully probed via of_find_net_device_by_node.

Andrew is wondering how to avoid those extra EPROBE_DEFER iterations.
It is weird that the entire functionality of the system depends on those
EPROBE_DEFERs, typically you'd expect that EPROBE_DEFER just serializes
asynchronous probing of drivers with interdependencies. But in this case
it serializes synchronous probing.
quoted
quoted
It is pretty hard to solve. An Ethernet interface can be used by the
kernel itself, e.g. NFS root. At the point you call register_netdev()
in the probe function, to register the interface with the core,
Are you using "ethernet interface" and "ethernet controller"
interchangeably? Looking at some other drivers, it looks like the
ethernet controlled (FEC) is what would call register_netdev(). So
what's wrong with that happening if switch0 has not probed
successfully?
The "interface" and "controller" terms are not really interchangeable,
an interface can also be virtual (stacked interfaces on top of physical
ones, like VLAN or DSA) while network controllers are typically physical
(unless emulated). But that is not of importance.

The context here is that you cannot solve the interdependency by
registering the DSA master (FEC) first, then its MDIO bus second (the
DSA switch probes on the DSA master's MDIO bus => if you do this,
of_find_net_device_by_node from the DSA layer would find its master the
first time). The reason you cannot do that is because you need the MDIO
bus for really basic stuff: you also have your Ethernet PHY on it, and
you need to initialize that in order to send traffic. And you need to be
able to send traffic as soon as register_netdev() completes.

So since the driver initialization sequence has a single written order
regardless of whether DSA switches are attached or not, that order is
picked to be the one where traffic works as soon as register_netdev completes.
quoted
quoted
it
needs to be fully ready to go.  The networking stack can start using
the interface before register_netdev() even returns. So you cannot
first register the interface and then register the MDIO bus.

I once looked to see if it was possible to tell the driver core to not
even bother probing a bus as soon as it is registered, go straight to
defer probe handling. Because this is one case we know it cannot
work. But it does not seem possible.
fw_devlink doesn't understand the "ethernet" property. If I add that,
then in the example you state above, switch0's probe won't even be
called until the FEC probe returns. The change is pretty trivial
(pasted below) -- can you try it out and tell me if it does what you
need/want?

-Saravana
+++ b/drivers/of/property.c
@@ -1292,6 +1292,7 @@ DEFINE_SIMPLE_PROP(resets, "resets", "#reset-cells")
 DEFINE_SIMPLE_PROP(leds, "leds", NULL)
 DEFINE_SIMPLE_PROP(backlight, "backlight", NULL)
 DEFINE_SIMPLE_PROP(phy_handle, "phy-handle", NULL)
+DEFINE_SIMPLE_PROP(ethernet, "ethernet", NULL)
 DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
 DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
@@ -1381,6 +1382,7 @@ static const struct supplier_bindings
of_supplier_bindings[] = {
        { .parse_prop = parse_leds, },
        { .parse_prop = parse_backlight, },
        { .parse_prop = parse_phy_handle, },
+       { .parse_prop = parse_ethernet, },
        { .parse_prop = parse_gpio_compat, },
        { .parse_prop = parse_interrupts, },
        { .parse_prop = parse_regulators, },
I don't have this exact setup to test, so I'll let Andrew do it, but I
have a question: DSA sets up a device link to its master in dsa_master_setup.
It does this to autoremove itself when the DSA master gets removed, but
fundamentally it does it after the entire EPROBE_DEFER shebang discussed
above has already happened. If your patch works, we can drop the manually
added device link, right?
Yes, you can drop that code if fw_devlink=on works and forms the links
correctly (it should and if not it shouldn't be hard to fix -- just
needs a bit of time to figure out why).
There's also the question of what to do in case of multiple DSA masters
Sigh... I'm out of my depth with all this network specific discussions
:( Especially when it comes to Linux network related frameworks.
(multiple "ethernet" properties). Right now, if you describe two DSA
masters in the device tree, DSA will pick the first DSA master and use
just that (it doesn't have full support for more than one). But even
though the second DSA master is not used for anything, with your change,
unbinding it will also unbind the switch, will it not?
I think I understand your question. And the answer is "yes". It
actually goes one step further. Until both all the DSA masters
(devices pointed to by the "ethernet" properties) of a DSA are bound
successfully, the DSA will not even be probed in the first place. You
can set come command line args to timeout waiting for suppliers with
missing drivers, but ideally you shouldn't need to use those
(fw_devlink is fairly new and I'm continuing to work on improving
it/making it work by default).

-Saravana
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help