diff options
| author | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 09:55:01 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 11:08:21 -0400 |
| commit | 4617721c502b2ddaa4e324e86da4997edf738fa5 (patch) | |
| tree | 350aa83b47da0b574a814795392b20b0bed83cbc /drivers/net/ppp/pppox.c | |
| download | linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip | |
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since
it can be declared outside of the ftrace.c code, it calls
ftrace_ops_init() on the ops in every global function. The issue is that
if two tasks call it on the same ops at the same time it is possible to
have the initialization of one corrupt the initialization of the other
call.
Create a ops_mutex to use to synchronize every initialization of the
ftrace_ops. The mutex is taken within checking the ftrace_ops flag that
states it was initializied but the flag is checked again after the mutex
has been taken. Checking first outside the mutex allows it to shortcut
having to take the mutex. But then the check needs to be done again after
the mute is taken in case of races.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home
Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock")
Reported-by: sashiko-bot@kernel.org
Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/net/ppp/pppox.c')
| -rw-r--r-- | drivers/net/ppp/pppox.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c new file mode 100644 index 000000000..a6f72c813 --- /dev/null +++ b/drivers/net/ppp/pppox.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** -*- linux-c -*- *********************************************************** + * Linux PPP over X/Ethernet (PPPoX/PPPoE) Sockets + * + * PPPoX --- Generic PPP encapsulation socket family + * PPPoE --- PPP over Ethernet (RFC 2516) + * + * Version: 0.5.2 + * + * Author: Michal Ostrowski <mostrows@speakeasy.net> + * + * 051000 : Initialization cleanup + * + * License: + */ + +#include <linux/string.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/net.h> +#include <linux/init.h> +#include <linux/if_pppox.h> +#include <linux/ppp_defs.h> +#include <linux/ppp-ioctl.h> +#include <linux/ppp_channel.h> +#include <linux/kmod.h> + +#include <net/sock.h> + +#include <linux/uaccess.h> + +static const struct pppox_proto *pppox_protos[PX_MAX_PROTO + 1]; + +int register_pppox_proto(int proto_num, const struct pppox_proto *pp) +{ + if (proto_num < 0 || proto_num > PX_MAX_PROTO) + return -EINVAL; + if (pppox_protos[proto_num]) + return -EALREADY; + pppox_protos[proto_num] = pp; + return 0; +} + +void unregister_pppox_proto(int proto_num) +{ + if (proto_num >= 0 && proto_num <= PX_MAX_PROTO) + pppox_protos[proto_num] = NULL; +} + +void pppox_unbind_sock(struct sock *sk) +{ + /* Clear connection to ppp device, if attached. */ + + if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) { + ppp_unregister_channel(&pppox_sk(sk)->chan); + sk->sk_state = PPPOX_DEAD; + } +} + +EXPORT_SYMBOL(register_pppox_proto); +EXPORT_SYMBOL(unregister_pppox_proto); +EXPORT_SYMBOL(pppox_unbind_sock); + +int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + int rc; + + lock_sock(sk); + + switch (cmd) { + case PPPIOCGCHAN: { + struct sk_buff *skb; + int index; + + rc = -ENOTCONN; + if (!(sk->sk_state & PPPOX_CONNECTED)) + break; + + rc = -EINVAL; + index = ppp_channel_index(&po->chan); + if (put_user(index , (int __user *) arg)) + break; + + rc = 0; + /* PPPIOCGCHAN historically marks the userspace handoff to + * generic PPP; pppd then attaches the returned channel to + * /dev/ppp. + */ + sk->sk_state |= PPPOX_BOUND; + /* Let lockless receive paths finish queueing against the old + * state. + */ + synchronize_net(); + /* Drain packets queued before the handoff because a bound + * socket is no longer readable. + */ + while ((skb = skb_dequeue(&sk->sk_receive_queue))) { + skb_orphan(skb); + ppp_input(&po->chan, skb); + } + break; + } + default: + rc = pppox_protos[sk->sk_protocol]->ioctl ? + pppox_protos[sk->sk_protocol]->ioctl(sock, cmd, arg) : -ENOTTY; + } + + release_sock(sk); + return rc; +} + +EXPORT_SYMBOL(pppox_ioctl); + +#ifdef CONFIG_COMPAT +int pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); +} + +EXPORT_SYMBOL(pppox_compat_ioctl); +#endif + +static int pppox_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + int rc = -EPROTOTYPE; + + if (protocol < 0 || protocol > PX_MAX_PROTO) + goto out; + + rc = -EPROTONOSUPPORT; + if (!pppox_protos[protocol]) + request_module("net-pf-%d-proto-%d", PF_PPPOX, protocol); + if (!pppox_protos[protocol] || + !try_module_get(pppox_protos[protocol]->owner)) + goto out; + + rc = pppox_protos[protocol]->create(net, sock, kern); + + module_put(pppox_protos[protocol]->owner); +out: + return rc; +} + +static const struct net_proto_family pppox_proto_family = { + .family = PF_PPPOX, + .create = pppox_create, + .owner = THIS_MODULE, +}; + +static int __init pppox_init(void) +{ + return sock_register(&pppox_proto_family); +} + +static void __exit pppox_exit(void) +{ + sock_unregister(PF_PPPOX); +} + +module_init(pppox_init); +module_exit(pppox_exit); + +MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>"); +MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_PPPOX); |
