[ethtool] ethtool: fix stack clash in do_get_phy_tunable and do_set_phy_tunable

Message ID 20180509120146.C7408A0C6F@unicorn.suse.cz
State Accepted
Delegated to: John Linville
Headers show
Series
  • [ethtool] ethtool: fix stack clash in do_get_phy_tunable and do_set_phy_tunable
Related show

Commit Message

Michal Kubecek May 9, 2018, 12:01 p.m.
Users reported stack clash detected when using --get-phy-tunable on
ppc64le. Problem is caused by local variable ds of type struct
ethtool_tunable which has last member "void *data[0]". Accessing data[0]
(as do_get_phy_tunable() does) or adding requested value at the end (which
is what kernel ioctl does) writes past allocated space for the variable.

Make ds part of an anonymous structure to make sure there is enough space
for tunable value and drop the (pointless) access to ds.data[0]. The same
problem also exists in do_set_phy_tunable().

Fixes: b0fe96dec90f ("Ethtool: Implements ETHTOOL_PHY_GTUNABLE/ETHTOOL_PHY_STUNABLE and PHY downshift")
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 ethtool.c | 39 +++++++++++++++++++++------------------
 1 file changed, 21 insertions(+), 18 deletions(-)

Comments

John W. Linville May 31, 2018, 5:51 p.m. | #1
On Wed, May 09, 2018 at 02:01:46PM +0200, Michal Kubecek wrote:
> Users reported stack clash detected when using --get-phy-tunable on
> ppc64le. Problem is caused by local variable ds of type struct
> ethtool_tunable which has last member "void *data[0]". Accessing data[0]
> (as do_get_phy_tunable() does) or adding requested value at the end (which
> is what kernel ioctl does) writes past allocated space for the variable.
> 
> Make ds part of an anonymous structure to make sure there is enough space
> for tunable value and drop the (pointless) access to ds.data[0]. The same
> problem also exists in do_set_phy_tunable().
> 
> Fixes: b0fe96dec90f ("Ethtool: Implements ETHTOOL_PHY_GTUNABLE/ETHTOOL_PHY_STUNABLE and PHY downshift")
> Signed-off-by: Michal Kubecek <mkubecek@suse.cz>

LGTM -- queued for next release...

Patch

diff --git a/ethtool.c b/ethtool.c
index 3289e0f6e8ec..2e873848eb4e 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4740,20 +4740,22 @@  static int do_get_phy_tunable(struct cmd_context *ctx)
 	}
 
 	if (downshift_changed) {
-		struct ethtool_tunable ds;
+		struct {
+			struct ethtool_tunable ds;
+			u8 __count;
+		} cont;
 		u8 count = 0;
 
-		ds.cmd = ETHTOOL_PHY_GTUNABLE;
-		ds.id = ETHTOOL_PHY_DOWNSHIFT;
-		ds.type_id = ETHTOOL_TUNABLE_U8;
-		ds.len = 1;
-		ds.data[0] = &count;
-		err = send_ioctl(ctx, &ds);
+		cont.ds.cmd = ETHTOOL_PHY_GTUNABLE;
+		cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
+		cont.ds.type_id = ETHTOOL_TUNABLE_U8;
+		cont.ds.len = 1;
+		err = send_ioctl(ctx, &cont.ds);
 		if (err < 0) {
 			perror("Cannot Get PHY downshift count");
 			return 87;
 		}
-		count = *((u8 *)&ds.data[0]);
+		count = *((u8 *)&cont.ds.data[0]);
 		if (count)
 			fprintf(stdout, "Downshift count: %d\n", count);
 		else
@@ -4931,16 +4933,17 @@  static int do_set_phy_tunable(struct cmd_context *ctx)
 
 	/* Do it */
 	if (ds_changed) {
-		struct ethtool_tunable ds;
-		u8 count;
-
-		ds.cmd = ETHTOOL_PHY_STUNABLE;
-		ds.id = ETHTOOL_PHY_DOWNSHIFT;
-		ds.type_id = ETHTOOL_TUNABLE_U8;
-		ds.len = 1;
-		ds.data[0] = &count;
-		*((u8 *)&ds.data[0]) = ds_cnt;
-		err = send_ioctl(ctx, &ds);
+		struct {
+			struct ethtool_tunable ds;
+			u8 __count;
+		} cont;
+
+		cont.ds.cmd = ETHTOOL_PHY_STUNABLE;
+		cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
+		cont.ds.type_id = ETHTOOL_TUNABLE_U8;
+		cont.ds.len = 1;
+		*((u8 *)&cont.ds.data[0]) = ds_cnt;
+		err = send_ioctl(ctx, &cont.ds);
 		if (err < 0) {
 			perror("Cannot Set PHY downshift count");
 			err = 87;