diff options
author | Felix Fietkau <nbd@openwrt.org> | 2015-12-23 11:15:02 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2015-12-23 11:15:02 +0000 |
commit | 720afadc7a5069c6580fd2b5140732565261bebf (patch) | |
tree | 70f1eb3594e9a01715c4a7a0b5772d683da8c48d /target/linux | |
parent | 8996164e5665fbe08919fd7ebf850b082f4e347a (diff) | |
download | mtk-20170518-720afadc7a5069c6580fd2b5140732565261bebf.zip mtk-20170518-720afadc7a5069c6580fd2b5140732565261bebf.tar.gz mtk-20170518-720afadc7a5069c6580fd2b5140732565261bebf.tar.bz2 |
kernel: backport all current pppoe kernel fixes to 3.18
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 47963
Diffstat (limited to 'target/linux')
10 files changed, 260 insertions, 20 deletions
diff --git a/target/linux/generic/patches-3.18/081-pppoe-Use-workqueue-to-die-properly-when-a-PADT-is-r.patch b/target/linux/generic/patches-3.18/081-01-pppoe-Use-workqueue-to-die-properly-when-a-PADT-is-r.patch index de85830..de85830 100644 --- a/target/linux/generic/patches-3.18/081-pppoe-Use-workqueue-to-die-properly-when-a-PADT-is-r.patch +++ b/target/linux/generic/patches-3.18/081-01-pppoe-Use-workqueue-to-die-properly-when-a-PADT-is-r.patch diff --git a/target/linux/generic/patches-3.18/081-02-pppoe-Lacks-DST-MAC-address-check.patch b/target/linux/generic/patches-3.18/081-02-pppoe-Lacks-DST-MAC-address-check.patch new file mode 100644 index 0000000..3ce778e --- /dev/null +++ b/target/linux/generic/patches-3.18/081-02-pppoe-Lacks-DST-MAC-address-check.patch @@ -0,0 +1,25 @@ +From: Joakim Tjernlund <Joakim.Tjernlund@transmode.se> +Date: Mon, 20 Apr 2015 21:07:48 +0200 +Subject: [PATCH] pppoe: Lacks DST MAC address check + +A pppoe session is identified by its session ID and MAC address. +Currently pppoe does not check if the received pkg has the correct +MAC address. This is a problem when the eth I/F is in promisc mode +as then any DST MAC address is accepted. + +Signed-off-by: Joakim Tjernlund <joakim.tjernlund@transmode.se> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -380,6 +380,9 @@ static int pppoe_rcv_core(struct sock *s + * can't change. + */ + ++ if (skb->pkt_type == PACKET_OTHERHOST) ++ goto abort_kfree; ++ + if (sk->sk_state & PPPOX_BOUND) { + ppp_input(&po->chan, skb); + } else if (sk->sk_state & PPPOX_RELAY) { diff --git a/target/linux/generic/patches-3.18/100-pppoe-drop-pppoe-device-in-pppoe_unbind_sock_work.patch b/target/linux/generic/patches-3.18/081-03-pppoe-drop-pppoe-device-in-pppoe_unbind_sock_work.patch index c461b3e..f672c67 100644 --- a/target/linux/generic/patches-3.18/100-pppoe-drop-pppoe-device-in-pppoe_unbind_sock_work.patch +++ b/target/linux/generic/patches-3.18/081-03-pppoe-drop-pppoe-device-in-pppoe_unbind_sock_work.patch @@ -1,5 +1,5 @@ From: Felix Fietkau <nbd@openwrt.org> -Date: Sat, 9 May 2015 23:03:47 +0200 +Date: Sat, 9 May 2015 23:08:38 +0200 Subject: [PATCH] pppoe: drop pppoe device in pppoe_unbind_sock_work After receiving a PADT and the socket is closed, user space will no @@ -10,11 +10,12 @@ This leads to errors like this: Fixes: 287f3a943fe ("pppoe: Use workqueue to die properly when a PADT is received") Signed-off-by: Felix Fietkau <nbd@openwrt.org> +Signed-off-by: David S. Miller <davem@davemloft.net> --- --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c -@@ -462,6 +462,10 @@ static void pppoe_unbind_sock_work(struc +@@ -465,6 +465,10 @@ static void pppoe_unbind_sock_work(struc struct sock *sk = sk_pppox(po); lock_sock(sk); diff --git a/target/linux/generic/patches-3.18/081-04-ppp-don-t-override-sk-sk_state-in-pppoe_flush_dev.patch b/target/linux/generic/patches-3.18/081-04-ppp-don-t-override-sk-sk_state-in-pppoe_flush_dev.patch new file mode 100644 index 0000000..73f2e3d --- /dev/null +++ b/target/linux/generic/patches-3.18/081-04-ppp-don-t-override-sk-sk_state-in-pppoe_flush_dev.patch @@ -0,0 +1,68 @@ +From: Guillaume Nault <g.nault@alphalink.fr> +Date: Wed, 30 Sep 2015 11:45:33 +0200 +Subject: [PATCH] ppp: don't override sk->sk_state in pppoe_flush_dev() + +Since commit 2b018d57ff18 ("pppoe: drop PPPOX_ZOMBIEs in pppoe_release"), +pppoe_release() calls dev_put(po->pppoe_dev) if sk is in the +PPPOX_ZOMBIE state. But pppoe_flush_dev() can set sk->sk_state to +PPPOX_ZOMBIE _and_ reset po->pppoe_dev to NULL. This leads to the +following oops: + +[ 570.140800] BUG: unable to handle kernel NULL pointer dereference at 00000000000004e0 +[ 570.142931] IP: [<ffffffffa018c701>] pppoe_release+0x50/0x101 [pppoe] +[ 570.144601] PGD 3d119067 PUD 3dbc1067 PMD 0 +[ 570.144601] Oops: 0000 [#1] SMP +[ 570.144601] Modules linked in: l2tp_ppp l2tp_netlink l2tp_core ip6_udp_tunnel udp_tunnel pppoe pppox ppp_generic slhc loop crc32c_intel ghash_clmulni_intel jitterentropy_rng sha256_generic hmac drbg ansi_cprng aesni_intel aes_x86_64 ablk_helper cryptd lrw gf128mul glue_helper acpi_cpufreq evdev serio_raw processor button ext4 crc16 mbcache jbd2 virtio_net virtio_blk virtio_pci virtio_ring virtio +[ 570.144601] CPU: 1 PID: 15738 Comm: ppp-apitest Not tainted 4.2.0 #1 +[ 570.144601] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Debian-1.8.2-1 04/01/2014 +[ 570.144601] task: ffff88003d30d600 ti: ffff880036b60000 task.ti: ffff880036b60000 +[ 570.144601] RIP: 0010:[<ffffffffa018c701>] [<ffffffffa018c701>] pppoe_release+0x50/0x101 [pppoe] +[ 570.144601] RSP: 0018:ffff880036b63e08 EFLAGS: 00010202 +[ 570.144601] RAX: 0000000000000000 RBX: ffff880034340000 RCX: 0000000000000206 +[ 570.144601] RDX: 0000000000000006 RSI: ffff88003d30dd20 RDI: ffff88003d30dd20 +[ 570.144601] RBP: ffff880036b63e28 R08: 0000000000000001 R09: 0000000000000000 +[ 570.144601] R10: 00007ffee9b50420 R11: ffff880034340078 R12: ffff8800387ec780 +[ 570.144601] R13: ffff8800387ec7b0 R14: ffff88003e222aa0 R15: ffff8800387ec7b0 +[ 570.144601] FS: 00007f5672f48700(0000) GS:ffff88003fc80000(0000) knlGS:0000000000000000 +[ 570.144601] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 570.144601] CR2: 00000000000004e0 CR3: 0000000037f7e000 CR4: 00000000000406a0 +[ 570.144601] Stack: +[ 570.144601] ffffffffa018f240 ffff8800387ec780 ffffffffa018f240 ffff8800387ec7b0 +[ 570.144601] ffff880036b63e48 ffffffff812caabe ffff880039e4e000 0000000000000008 +[ 570.144601] ffff880036b63e58 ffffffff812cabad ffff880036b63ea8 ffffffff811347f5 +[ 570.144601] Call Trace: +[ 570.144601] [<ffffffff812caabe>] sock_release+0x1a/0x75 +[ 570.144601] [<ffffffff812cabad>] sock_close+0xd/0x11 +[ 570.144601] [<ffffffff811347f5>] __fput+0xff/0x1a5 +[ 570.144601] [<ffffffff811348cb>] ____fput+0x9/0xb +[ 570.144601] [<ffffffff81056682>] task_work_run+0x66/0x90 +[ 570.144601] [<ffffffff8100189e>] prepare_exit_to_usermode+0x8c/0xa7 +[ 570.144601] [<ffffffff81001a26>] syscall_return_slowpath+0x16d/0x19b +[ 570.144601] [<ffffffff813babb1>] int_ret_from_sys_call+0x25/0x9f +[ 570.144601] Code: 48 8b 83 c8 01 00 00 a8 01 74 12 48 89 df e8 8b 27 14 e1 b8 f7 ff ff ff e9 b7 00 00 00 8a 43 12 a8 0b 74 1c 48 8b 83 a8 04 00 00 <48> 8b 80 e0 04 00 00 65 ff 08 48 c7 83 a8 04 00 00 00 00 00 00 +[ 570.144601] RIP [<ffffffffa018c701>] pppoe_release+0x50/0x101 [pppoe] +[ 570.144601] RSP <ffff880036b63e08> +[ 570.144601] CR2: 00000000000004e0 +[ 570.200518] ---[ end trace 46956baf17349563 ]--- + +pppoe_flush_dev() has no reason to override sk->sk_state with +PPPOX_ZOMBIE. pppox_unbind_sock() already sets sk->sk_state to +PPPOX_DEAD, which is the correct state given that sk is unbound and +po->pppoe_dev is NULL. + +Fixes: 2b018d57ff18 ("pppoe: drop PPPOX_ZOMBIEs in pppoe_release") +Tested-by: Oleksii Berezhniak <core@irc.lg.ua> +Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -313,7 +313,6 @@ static void pppoe_flush_dev(struct net_d + if (po->pppoe_dev == dev && + sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { + pppox_unbind_sock(sk); +- sk->sk_state = PPPOX_ZOMBIE; + sk->sk_state_change(sk); + po->pppoe_dev = NULL; + dev_put(dev); diff --git a/target/linux/generic/patches-3.18/081-05-ppp-fix-pppoe_dev-deletion-condition-in-pppoe_releas.patch b/target/linux/generic/patches-3.18/081-05-ppp-fix-pppoe_dev-deletion-condition-in-pppoe_releas.patch new file mode 100644 index 0000000..b1ae60b --- /dev/null +++ b/target/linux/generic/patches-3.18/081-05-ppp-fix-pppoe_dev-deletion-condition-in-pppoe_releas.patch @@ -0,0 +1,29 @@ +From: Guillaume Nault <g.nault@alphalink.fr> +Date: Thu, 22 Oct 2015 16:57:10 +0200 +Subject: [PATCH] ppp: fix pppoe_dev deletion condition in pppoe_release() + +We can't rely on PPPOX_ZOMBIE to decide whether to clear po->pppoe_dev. +PPPOX_ZOMBIE can be set by pppoe_disc_rcv() even when po->pppoe_dev is +NULL. So we have no guarantee that (sk->sk_state & PPPOX_ZOMBIE) implies +(po->pppoe_dev != NULL). +Since we're releasing a PPPoE socket, we want to release the pppoe_dev +if it exists and reset sk_state to PPPOX_DEAD, no matter the previous +value of sk_state. So we can just check for po->pppoe_dev and avoid any +assumption on sk->sk_state. + +Fixes: 2b018d57ff18 ("pppoe: drop PPPOX_ZOMBIEs in pppoe_release") +Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -589,7 +589,7 @@ static int pppoe_release(struct socket * + + po = pppox_sk(sk); + +- if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { ++ if (po->pppoe_dev) { + dev_put(po->pppoe_dev); + po->pppoe_dev = NULL; + } diff --git a/target/linux/generic/patches-3.18/081-06-ppp-don-t-set-sk_state-to-PPPOX_ZOMBIE-in-pppoe_disc.patch b/target/linux/generic/patches-3.18/081-06-ppp-don-t-set-sk_state-to-PPPOX_ZOMBIE-in-pppoe_disc.patch new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/target/linux/generic/patches-3.18/081-06-ppp-don-t-set-sk_state-to-PPPOX_ZOMBIE-in-pppoe_disc.patch diff --git a/target/linux/generic/patches-3.18/081-07-ppp-remove-PPPOX_ZOMBIE-socket-state.patch b/target/linux/generic/patches-3.18/081-07-ppp-remove-PPPOX_ZOMBIE-socket-state.patch new file mode 100644 index 0000000..ffdba4d --- /dev/null +++ b/target/linux/generic/patches-3.18/081-07-ppp-remove-PPPOX_ZOMBIE-socket-state.patch @@ -0,0 +1,51 @@ +From: Guillaume Nault <g.nault@alphalink.fr> +Date: Thu, 19 Nov 2015 12:53:21 +0100 +Subject: [PATCH] ppp: remove PPPOX_ZOMBIE socket state + +PPPOX_ZOMBIE is never set anymore. + +Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -311,7 +311,7 @@ static void pppoe_flush_dev(struct net_d + lock_sock(sk); + + if (po->pppoe_dev == dev && +- sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { ++ sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + pppox_unbind_sock(sk); + sk->sk_state_change(sk); + po->pppoe_dev = NULL; +@@ -775,7 +775,7 @@ static int pppoe_ioctl(struct socket *so + struct pppox_sock *relay_po; + + err = -EBUSY; +- if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD)) ++ if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD)) + break; + + err = -ENOTCONN; +--- a/drivers/net/ppp/pppox.c ++++ b/drivers/net/ppp/pppox.c +@@ -58,7 +58,7 @@ void pppox_unbind_sock(struct sock *sk) + { + /* Clear connection to ppp device, if attached. */ + +- if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) { ++ if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) { + ppp_unregister_channel(&pppox_sk(sk)->chan); + sk->sk_state = PPPOX_DEAD; + } +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -91,7 +91,6 @@ enum { + PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */ + PPPOX_BOUND = 2, /* bound to ppp device */ + PPPOX_RELAY = 4, /* forwarding is enabled */ +- PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */ + PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/ + }; + diff --git a/target/linux/generic/patches-3.18/081-08-pppoe-fix-memory-corruption-in-padt-work-structure.patch b/target/linux/generic/patches-3.18/081-08-pppoe-fix-memory-corruption-in-padt-work-structure.patch new file mode 100644 index 0000000..147e971 --- /dev/null +++ b/target/linux/generic/patches-3.18/081-08-pppoe-fix-memory-corruption-in-padt-work-structure.patch @@ -0,0 +1,82 @@ +From: Guillaume Nault <g.nault@alphalink.fr> +Date: Thu, 3 Dec 2015 16:49:32 +0100 +Subject: [PATCH] pppoe: fix memory corruption in padt work structure + +pppoe_connect() mustn't touch the padt_work field of pppoe sockets +because that work could be already pending. + +[ 21.473147] BUG: unable to handle kernel NULL pointer dereference at 00000004 +[ 21.474523] IP: [<c1043177>] process_one_work+0x29/0x31c +[ 21.475164] *pde = 00000000 +[ 21.475513] Oops: 0000 [#1] SMP +[ 21.475910] Modules linked in: pppoe pppox ppp_generic slhc crc32c_intel aesni_intel virtio_net xts aes_i586 lrw gf128mul ablk_helper cryptd evdev acpi_cpufreq processor serio_raw button ext4 crc16 mbcache jbd2 virtio_blk virtio_pci virtio_ring virtio +[ 21.476168] CPU: 2 PID: 164 Comm: kworker/2:2 Not tainted 4.4.0-rc1 #1 +[ 21.476168] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Debian-1.8.2-1 04/01/2014 +[ 21.476168] task: f5f83c00 ti: f5e28000 task.ti: f5e28000 +[ 21.476168] EIP: 0060:[<c1043177>] EFLAGS: 00010046 CPU: 2 +[ 21.476168] EIP is at process_one_work+0x29/0x31c +[ 21.484082] EAX: 00000000 EBX: f678b2a0 ECX: 00000004 EDX: 00000000 +[ 21.484082] ESI: f6c69940 EDI: f5e29ef0 EBP: f5e29f0c ESP: f5e29edc +[ 21.484082] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 +[ 21.484082] CR0: 80050033 CR2: 000000a4 CR3: 317ad000 CR4: 00040690 +[ 21.484082] Stack: +[ 21.484082] 00000000 f6c69950 00000000 f6c69940 c0042338 f5e29f0c c1327945 00000000 +[ 21.484082] 00000008 f678b2a0 f6c69940 f678b2b8 f5e29f30 c1043984 f5f83c00 f6c69970 +[ 21.484082] f678b2a0 c10437d3 f6775e80 f678b2a0 c10437d3 f5e29fac c1047059 f5e29f74 +[ 21.484082] Call Trace: +[ 21.484082] [<c1327945>] ? _raw_spin_lock_irq+0x28/0x30 +[ 21.484082] [<c1043984>] worker_thread+0x1b1/0x244 +[ 21.484082] [<c10437d3>] ? rescuer_thread+0x229/0x229 +[ 21.484082] [<c10437d3>] ? rescuer_thread+0x229/0x229 +[ 21.484082] [<c1047059>] kthread+0x8f/0x94 +[ 21.484082] [<c1327a32>] ? _raw_spin_unlock_irq+0x22/0x26 +[ 21.484082] [<c1327ee9>] ret_from_kernel_thread+0x21/0x38 +[ 21.484082] [<c1046fca>] ? kthread_parkme+0x19/0x19 +[ 21.496082] Code: 5d c3 55 89 e5 57 56 53 89 c3 83 ec 24 89 d0 89 55 e0 8d 7d e4 e8 6c d8 ff ff b9 04 00 00 00 89 45 d8 8b 43 24 89 45 dc 8b 45 d8 <8b> 40 04 8b 80 e0 00 00 00 c1 e8 05 24 01 88 45 d7 8b 45 e0 8d +[ 21.496082] EIP: [<c1043177>] process_one_work+0x29/0x31c SS:ESP 0068:f5e29edc +[ 21.496082] CR2: 0000000000000004 +[ 21.496082] ---[ end trace e362cc9cf10dae89 ]--- + +Reported-by: Andrew <nitr0@seti.kr.ua> +Fixes: 287f3a943fef ("pppoe: Use workqueue to die properly when a PADT is received") +Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -550,6 +550,9 @@ static int pppoe_create(struct net *net, + sk->sk_family = PF_PPPOX; + sk->sk_protocol = PX_PROTO_OE; + ++ INIT_WORK(&pppox_sk(sk)->proto.pppoe.padt_work, ++ pppoe_unbind_sock_work); ++ + return 0; + } + +@@ -614,8 +617,6 @@ static int pppoe_connect(struct socket * + + lock_sock(sk); + +- INIT_WORK(&po->proto.pppoe.padt_work, pppoe_unbind_sock_work); +- + error = -EINVAL; + if (sp->sa_protocol != PX_PROTO_OE) + goto end; +@@ -645,8 +646,13 @@ static int pppoe_connect(struct socket * + po->pppoe_dev = NULL; + } + +- memset(sk_pppox(po) + 1, 0, +- sizeof(struct pppox_sock) - sizeof(struct sock)); ++ po->pppoe_ifindex = 0; ++ memset(&po->pppoe_pa, 0, sizeof(po->pppoe_pa)); ++ memset(&po->pppoe_relay, 0, sizeof(po->pppoe_relay)); ++ memset(&po->chan, 0, sizeof(po->chan)); ++ po->next = NULL; ++ po->num = 0; ++ + sk->sk_state = PPPOX_NONE; + } + diff --git a/target/linux/generic/patches-3.18/101-pppoe-fix-disconnect-crash.patch b/target/linux/generic/patches-3.18/101-pppoe-fix-disconnect-crash.patch deleted file mode 100644 index f2e6e45..0000000 --- a/target/linux/generic/patches-3.18/101-pppoe-fix-disconnect-crash.patch +++ /dev/null @@ -1,16 +0,0 @@ -Fix crash with actions performed on the underlying interface (MAC address, -MTU or link state update). This triggers pppoe_flush_dev(), which cleans up -the device without announcing it in sk->sk_state. - -Patch by Guillaume Nault (pulled from netdev@vger) - ---- a/drivers/net/ppp/pppoe.c -+++ b/drivers/net/ppp/pppoe.c -@@ -313,7 +313,6 @@ static void pppoe_flush_dev(struct net_d - if (po->pppoe_dev == dev && - sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { - pppox_unbind_sock(sk); -- sk->sk_state = PPPOX_ZOMBIE; - sk->sk_state_change(sk); - po->pppoe_dev = NULL; - dev_put(dev); diff --git a/target/linux/generic/patches-3.18/650-pppoe_header_pad.patch b/target/linux/generic/patches-3.18/650-pppoe_header_pad.patch index 4b623fa..eb9b4bf 100644 --- a/target/linux/generic/patches-3.18/650-pppoe_header_pad.patch +++ b/target/linux/generic/patches-3.18/650-pppoe_header_pad.patch @@ -1,6 +1,6 @@ --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c -@@ -868,7 +868,7 @@ static int pppoe_sendmsg(struct kiocb *i +@@ -859,7 +859,7 @@ static int pppoe_sendmsg(struct kiocb *i goto end; @@ -9,7 +9,7 @@ 0, GFP_KERNEL); if (!skb) { error = -ENOMEM; -@@ -876,7 +876,7 @@ static int pppoe_sendmsg(struct kiocb *i +@@ -867,7 +867,7 @@ static int pppoe_sendmsg(struct kiocb *i } /* Reserve space for headers. */ |