diff mbox series

[iproute2,v2] json_print: Fix hidden 64-bit type promotion

Message ID 20180425143022.6986-1-toke@toke.dk
State Superseded, archived
Delegated to: stephen hemminger
Headers show
Series [iproute2,v2] json_print: Fix hidden 64-bit type promotion | expand

Commit Message

Toke Høiland-Jørgensen April 25, 2018, 2:30 p.m. UTC
print_uint() will silently promote its variable type to uint64_t, but there
is nothing that ensures that the format string specifier passed along with
it fits (and the function name suggest to pass "%u").

Fix this by changing print_uint() to use a native 'unsigned int' type, and
introduce a separate print_u64() function for printing 64-bit values. All
call sites that were actually printing 64-bit values using print_uint() are
converted to use print_u64() instead.

Since print_int() was already using native int types, just add a
print_s64() to match, but don't convert any call sites.

Cc: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
---
Changes since v1 (which was sent by Kevin Darbyshire-Bryant):
  - Add print_u64() and convert call sites that were actually passing
    64-bit values to print_uint().
  
 include/json_print.h  |  4 ++-
 include/json_writer.h | 12 ++++++---
 ip/ipaddress.c        | 62 +++++++++++++++++++++----------------------
 ip/ipmacsec.c         |  8 +++---
 ip/ipmroute.c         |  6 ++---
 lib/json_print.c      |  4 ++-
 lib/json_writer.c     | 30 ++++++++++++++++++---
 7 files changed, 78 insertions(+), 48 deletions(-)

Comments

Stephen Hemminger April 25, 2018, 2:55 p.m. UTC | #1
On Wed, 25 Apr 2018 16:30:22 +0200
Toke Høiland-Jørgensen <toke@toke.dk> wrote:

> print_uint() will silently promote its variable type to uint64_t, but there
> is nothing that ensures that the format string specifier passed along with
> it fits (and the function name suggest to pass "%u").
> 
> Fix this by changing print_uint() to use a native 'unsigned int' type, and
> introduce a separate print_u64() function for printing 64-bit values. All
> call sites that were actually printing 64-bit values using print_uint() are
> converted to use print_u64() instead.
> 
> Since print_int() was already using native int types, just add a
> print_s64() to match, but don't convert any call sites.
> 
> Cc: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
> Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>

Yes, this makes sense. Maybe there should be a print_luint for consistency.
Also, I tried (in vain) to make a version that allows GCC to check the
format string.  But it was a struggle and just gave up.
Toke Høiland-Jørgensen April 25, 2018, 2:57 p.m. UTC | #2
Stephen Hemminger <stephen@networkplumber.org> writes:

> On Wed, 25 Apr 2018 16:30:22 +0200
> Toke Høiland-Jørgensen <toke@toke.dk> wrote:
>
>> print_uint() will silently promote its variable type to uint64_t, but there
>> is nothing that ensures that the format string specifier passed along with
>> it fits (and the function name suggest to pass "%u").
>> 
>> Fix this by changing print_uint() to use a native 'unsigned int' type, and
>> introduce a separate print_u64() function for printing 64-bit values. All
>> call sites that were actually printing 64-bit values using print_uint() are
>> converted to use print_u64() instead.
>> 
>> Since print_int() was already using native int types, just add a
>> print_s64() to match, but don't convert any call sites.
>> 
>> Cc: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
>> Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
>
> Yes, this makes sense. Maybe there should be a print_luint for
> consistency.

I just realised I missed a few call sites, so I'll resend. Should I
call the new function print_luint() instead of print_u64()?

> Also, I tried (in vain) to make a version that allows GCC to check the
> format string.  But it was a struggle and just gave up.

Yeah, no idea how to do this either...

-Toke
Stephen Hemminger April 25, 2018, 3:12 p.m. UTC | #3
On Wed, 25 Apr 2018 16:57:52 +0200
Toke Høiland-Jørgensen <toke@toke.dk> wrote:

> Stephen Hemminger <stephen@networkplumber.org> writes:
> 
> > On Wed, 25 Apr 2018 16:30:22 +0200
> > Toke Høiland-Jørgensen <toke@toke.dk> wrote:
> >  
> >> print_uint() will silently promote its variable type to uint64_t, but there
> >> is nothing that ensures that the format string specifier passed along with
> >> it fits (and the function name suggest to pass "%u").
> >> 
> >> Fix this by changing print_uint() to use a native 'unsigned int' type, and
> >> introduce a separate print_u64() function for printing 64-bit values. All
> >> call sites that were actually printing 64-bit values using print_uint() are
> >> converted to use print_u64() instead.
> >> 
> >> Since print_int() was already using native int types, just add a
> >> print_s64() to match, but don't convert any call sites.
> >> 
> >> Cc: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
> >> Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>  
> >
> > Yes, this makes sense. Maybe there should be a print_luint for
> > consistency.  
> 
> I just realised I missed a few call sites, so I'll resend. Should I
> call the new function print_luint() instead of print_u64()?

Ideally, there would be both functions, and use based on what is being printed.

> > Also, I tried (in vain) to make a version that allows GCC to check the
> > format string.  But it was a struggle and just gave up.  
> 
> Yeah, no idea how to do this either...

Maybe some magic smatch or multi-line regex it would be possible to
find all instances of print_uint, then look at format string of each and see if there is
a single %u.  Some added complexity since some places only print json and don't care
and pass NULL for format.
diff mbox series

Patch

diff --git a/include/json_print.h b/include/json_print.h
index 2ca7830a..4677618e 100644
--- a/include/json_print.h
+++ b/include/json_print.h
@@ -56,10 +56,12 @@  void close_json_array(enum output_type type, const char *delim);
 		print_color_##type_name(t, COLOR_NONE, key, fmt, value);	\
 	}
 _PRINT_FUNC(int, int);
+_PRINT_FUNC(s64, int64_t);
 _PRINT_FUNC(bool, bool);
 _PRINT_FUNC(null, const char*);
 _PRINT_FUNC(string, const char*);
-_PRINT_FUNC(uint, uint64_t);
+_PRINT_FUNC(uint, unsigned int);
+_PRINT_FUNC(u64, uint64_t);
 _PRINT_FUNC(hu, unsigned short);
 _PRINT_FUNC(hex, unsigned int);
 _PRINT_FUNC(0xhex, unsigned int);
diff --git a/include/json_writer.h b/include/json_writer.h
index 4b4dec28..9d3f37f8 100644
--- a/include/json_writer.h
+++ b/include/json_writer.h
@@ -34,10 +34,12 @@  void jsonw_string(json_writer_t *self, const char *value);
 void jsonw_bool(json_writer_t *self, bool value);
 void jsonw_float(json_writer_t *self, double number);
 void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
-void jsonw_uint(json_writer_t *self, uint64_t number);
+void jsonw_uint(json_writer_t *self, unsigned int number);
+void jsonw_u64(json_writer_t *self, uint64_t number);
 void jsonw_xint(json_writer_t *self, uint64_t number);
 void jsonw_hu(json_writer_t *self, unsigned short number);
-void jsonw_int(json_writer_t *self, int64_t number);
+void jsonw_int(json_writer_t *self, int number);
+void jsonw_s64(json_writer_t *self, int64_t number);
 void jsonw_null(json_writer_t *self);
 void jsonw_lluint(json_writer_t *self, unsigned long long int num);
 
@@ -45,10 +47,12 @@  void jsonw_lluint(json_writer_t *self, unsigned long long int num);
 void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
 void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
 void jsonw_float_field(json_writer_t *self, const char *prop, double num);
-void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num);
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num);
 void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num);
 void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
-void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num);
+void jsonw_int_field(json_writer_t *self, const char *prop, int num);
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num);
 void jsonw_null_field(json_writer_t *self, const char *prop);
 void jsonw_lluint_field(json_writer_t *self, const char *prop,
 			unsigned long long int num);
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index aecc9a1d..75539e05 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -554,21 +554,21 @@  static void print_vf_stats64(FILE *fp, struct rtattr *vfstats)
 
 		/* RX stats */
 		open_json_object("rx");
-		print_uint(PRINT_JSON, "bytes", NULL,
+		print_u64(PRINT_JSON, "bytes", NULL,
 			   rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
-		print_uint(PRINT_JSON, "packets", NULL,
+		print_u64(PRINT_JSON, "packets", NULL,
 			   rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
-		print_uint(PRINT_JSON, "multicast", NULL,
+		print_u64(PRINT_JSON, "multicast", NULL,
 			   rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
-		print_uint(PRINT_JSON, "broadcast", NULL,
+		print_u64(PRINT_JSON, "broadcast", NULL,
 			   rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
 		close_json_object();
 
 		/* TX stats */
 		open_json_object("tx");
-		print_uint(PRINT_JSON, "tx_bytes", NULL,
+		print_u64(PRINT_JSON, "tx_bytes", NULL,
 			   rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
-		print_uint(PRINT_JSON, "tx_packets", NULL,
+		print_u64(PRINT_JSON, "tx_packets", NULL,
 			   rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
 		close_json_object();
 		close_json_object();
@@ -608,69 +608,69 @@  static void __print_link_stats(FILE *fp, struct rtattr *tb[])
 
 		/* RX stats */
 		open_json_object("rx");
-		print_uint(PRINT_JSON, "bytes", NULL, s->rx_bytes);
-		print_uint(PRINT_JSON, "packets", NULL, s->rx_packets);
-		print_uint(PRINT_JSON, "errors", NULL, s->rx_errors);
-		print_uint(PRINT_JSON, "dropped", NULL, s->rx_dropped);
-		print_uint(PRINT_JSON, "over_errors", NULL, s->rx_over_errors);
-		print_uint(PRINT_JSON, "multicast", NULL, s->multicast);
+		print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, s->rx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, s->rx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped);
+		print_u64(PRINT_JSON, "over_errors", NULL, s->rx_over_errors);
+		print_u64(PRINT_JSON, "multicast", NULL, s->multicast);
 		if (s->rx_compressed)
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "compressed", NULL, s->rx_compressed);
 
 		/* RX error stats */
 		if (show_stats > 1) {
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "length_errors",
 				   NULL, s->rx_length_errors);
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "crc_errors",
 				   NULL, s->rx_crc_errors);
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "frame_errors",
 				   NULL, s->rx_frame_errors);
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "fifo_errors",
 				   NULL, s->rx_fifo_errors);
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "missed_errors",
 				   NULL, s->rx_missed_errors);
 			if (s->rx_nohandler)
-				print_uint(PRINT_JSON,
+				print_u64(PRINT_JSON,
 					   "nohandler", NULL, s->rx_nohandler);
 		}
 		close_json_object();
 
 		/* TX stats */
 		open_json_object("tx");
-		print_uint(PRINT_JSON, "bytes", NULL, s->tx_bytes);
-		print_uint(PRINT_JSON, "packets", NULL, s->tx_packets);
-		print_uint(PRINT_JSON, "errors", NULL, s->tx_errors);
-		print_uint(PRINT_JSON, "dropped", NULL, s->tx_dropped);
-		print_uint(PRINT_JSON,
+		print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, s->tx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, s->tx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped);
+		print_u64(PRINT_JSON,
 			   "carrier_errors",
 			   NULL, s->tx_carrier_errors);
-		print_uint(PRINT_JSON, "collisions", NULL, s->collisions);
+		print_u64(PRINT_JSON, "collisions", NULL, s->collisions);
 		if (s->tx_compressed)
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "compressed", NULL, s->tx_compressed);
 
 		/* TX error stats */
 		if (show_stats > 1) {
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "aborted_errors",
 				   NULL, s->tx_aborted_errors);
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "fifo_errors",
 				   NULL, s->tx_fifo_errors);
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "window_errors",
 				   NULL, s->tx_window_errors);
-			print_uint(PRINT_JSON,
+			print_u64(PRINT_JSON,
 				   "heartbeat_errors",
 				   NULL, s->tx_heartbeat_errors);
 			if (carrier_changes)
-				print_uint(PRINT_JSON, "carrier_changes", NULL,
+				print_u64(PRINT_JSON, "carrier_changes", NULL,
 					   rta_getattr_u32(carrier_changes));
 		}
 
diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c
index 38ec7136..4e4e158e 100644
--- a/ip/ipmacsec.c
+++ b/ip/ipmacsec.c
@@ -640,7 +640,7 @@  static void print_attrs(struct rtattr *attrs[])
 	}
 }
 
-static __u64 getattr_uint(struct rtattr *stat)
+static __u64 getattr_u64(struct rtattr *stat)
 {
 	switch (RTA_PAYLOAD(stat)) {
 	case sizeof(__u64):
@@ -681,7 +681,7 @@  static void print_fp_stats(const char *prefix,
 
 		pad = strlen(names[i]) + 1;
 		if (stats[i])
-			printf("%*llu", pad, getattr_uint(stats[i]));
+			printf("%*llu", pad, getattr_u64(stats[i]));
 		else
 			printf("%*c", pad, '-');
 	}
@@ -697,8 +697,8 @@  static void print_json_stats(const char *names[], unsigned int num,
 		if (!names[i] || !stats[i])
 			continue;
 
-		print_uint(PRINT_JSON, names[i],
-			   NULL, getattr_uint(stats[i]));
+		print_u64(PRINT_JSON, names[i],
+			   NULL, getattr_u64(stats[i]));
 	}
 }
 
diff --git a/ip/ipmroute.c b/ip/ipmroute.c
index 59c5b771..cdb4d898 100644
--- a/ip/ipmroute.c
+++ b/ip/ipmroute.c
@@ -182,12 +182,12 @@  int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
 		struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]);
 
 		print_string(PRINT_FP, NULL, "%s", _SL_);
-		print_uint(PRINT_ANY, "packets", "  %"PRIu64" packets,",
+		print_u64(PRINT_ANY, "packets", "  %"PRIu64" packets,",
 			   mfcs->mfcs_packets);
-		print_uint(PRINT_ANY, "bytes", " %"PRIu64" bytes", mfcs->mfcs_bytes);
+		print_u64(PRINT_ANY, "bytes", " %"PRIu64" bytes", mfcs->mfcs_bytes);
 
 		if (mfcs->mfcs_wrong_if)
-			print_uint(PRINT_ANY, "wrong_if",
+			print_u64(PRINT_ANY, "wrong_if",
 				   ", %"PRIu64" arrived on wrong iif.",
 				   mfcs->mfcs_wrong_if);
 	}
diff --git a/lib/json_print.c b/lib/json_print.c
index bda72933..7a1cfa57 100644
--- a/lib/json_print.c
+++ b/lib/json_print.c
@@ -116,8 +116,10 @@  void close_json_array(enum output_type type, const char *str)
 		}							\
 	}
 _PRINT_FUNC(int, int);
+_PRINT_FUNC(s64, int64_t);
 _PRINT_FUNC(hu, unsigned short);
-_PRINT_FUNC(uint, uint64_t);
+_PRINT_FUNC(uint, unsigned int);
+_PRINT_FUNC(u64, uint64_t);
 _PRINT_FUNC(lluint, unsigned long long int);
 _PRINT_FUNC(float, double);
 #undef _PRINT_FUNC
diff --git a/lib/json_writer.c b/lib/json_writer.c
index 0ad04218..dc2fdd49 100644
--- a/lib/json_writer.c
+++ b/lib/json_writer.c
@@ -220,7 +220,12 @@  void jsonw_hu(json_writer_t *self, unsigned short num)
 	jsonw_printf(self, "%hu", num);
 }
 
-void jsonw_uint(json_writer_t *self, uint64_t num)
+void jsonw_uint(json_writer_t *self, unsigned int num)
+{
+	jsonw_printf(self, "%u", num);
+}
+
+void jsonw_u64(json_writer_t *self, uint64_t num)
 {
 	jsonw_printf(self, "%"PRIu64, num);
 }
@@ -235,7 +240,12 @@  void jsonw_lluint(json_writer_t *self, unsigned long long int num)
 	jsonw_printf(self, "%llu", num);
 }
 
-void jsonw_int(json_writer_t *self, int64_t num)
+void jsonw_int(json_writer_t *self, int num)
+{
+	jsonw_printf(self, "%d", num);
+}
+
+void jsonw_s64(json_writer_t *self, int64_t num)
 {
 	jsonw_printf(self, "%"PRId64, num);
 }
@@ -268,12 +278,18 @@  void jsonw_float_field_fmt(json_writer_t *self,
 	jsonw_float_fmt(self, fmt, val);
 }
 
-void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num)
 {
 	jsonw_name(self, prop);
 	jsonw_uint(self, num);
 }
 
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num)
+{
+	jsonw_name(self, prop);
+	jsonw_u64(self, num);
+}
+
 void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num)
 {
 	jsonw_name(self, prop);
@@ -294,12 +310,18 @@  void jsonw_lluint_field(json_writer_t *self,
 	jsonw_lluint(self, num);
 }
 
-void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
+void jsonw_int_field(json_writer_t *self, const char *prop, int num)
 {
 	jsonw_name(self, prop);
 	jsonw_int(self, num);
 }
 
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num)
+{
+	jsonw_name(self, prop);
+	jsonw_s64(self, num);
+}
+
 void jsonw_null_field(json_writer_t *self, const char *prop)
 {
 	jsonw_name(self, prop);