Prevent segfault in range encoding

* Explicitly check when ARFCN array split is impossible and return
  gracefully instead of using negative index.
* Separate range encoding into generic function and use it for all
  SI-related things.
* Propagate the error into that function and to its callers.
* Add separate test-case for the segfault previously triggered by this bug.

Change-Id: I3e049ab2d7c1c4d6c791b148f37e10636a8e43e0
Related: RT#7379
diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c
index 1a5e435..fc0282e 100644
--- a/openbsc/src/libbsc/rest_octets.c
+++ b/openbsc/src/libbsc/rest_octets.c
@@ -180,10 +180,10 @@
 	bitvec_set_bit(bv, L);
 }
 
-static inline void append_uarfcn(struct bitvec *bv, const uint16_t *u,
+static inline int append_uarfcn(struct bitvec *bv, const uint16_t *u,
 				 const uint16_t *sc, size_t length)
 {
-	int f0_inc, i, arfcns_used, w[RANGE_ENC_MAX_ARFCNS], a[length];
+	int f0_inc, i, w[RANGE_ENC_MAX_ARFCNS] = { 0 }, a[length];
 	uint8_t chan_list[16] = {0};
 
 	/* 3G Neighbour Cell Description */
@@ -211,9 +211,9 @@
 	/* Note: we do not support multiple UARFCN values ATM: */
 	bitvec_set_uint(bv, u[0], 14);
 
-	arfcns_used = range_enc_filter_arfcns(a, length, 0, &f0_inc);
-	range_enc_arfcns(ARFCN_RANGE_1024, a, arfcns_used, w, 0);
-	range_enc_range1024(chan_list, 0, f0_inc, w);
+	f0_inc = range_encode(ARFCN_RANGE_1024, a, length, w, 0, chan_list);
+	if (f0_inc < 0)
+		return f0_inc;
 
 	/* FDD_Indic0: parameter value '0000000000' is not a member of the set */
 	bitvec_set_bit(bv, f0_inc);
@@ -229,12 +229,15 @@
 
 	/* UTRAN TDD Description */
 	bitvec_set_bit(bv, 0);
+
+	return 0;
 }
 
 /* generate SI2quater rest octets: 3GPP TS 44.018 § 10.5.2.33b */
 int rest_octets_si2quater(uint8_t *data, const struct osmo_earfcn_si2q *e,
 			  const uint16_t *u, const uint16_t *sc, size_t u_len)
 {
+	int rc;
 	unsigned sz;
 	struct bitvec bv;
 	bv.data = data;
@@ -279,7 +282,13 @@
 			     SI2Q_MAX_LEN);
 			return -ENOMEM;
 		}
-		append_uarfcn(&bv, u, sc, u_len);
+		rc = append_uarfcn(&bv, u, sc, u_len);
+		if (rc < 0) {
+			LOGP(DRR, LOGL_ERROR, "SI2quater: failed to append %zu "
+			     "UARFCNs due to range encoding failure: %s\n",
+			     u_len, strerror(-rc));
+			return rc;
+		}
 	} else { /* No 3G Neighbour Cell Description */
 		bitvec_set_bit(&bv, 0);
 	}