sdr: Fix segfault on invalid unit types

The program would crash if the BMC returned an out of range (>90)
unit type for a full sensor record. This commit adds a range check
and also add support for IPMI 2.0 additional unit types 91 and 92
("fatal error" and "grams").

Resolves ipmitool/ipmitool#118

Signed-off-by: Alexander Amelkin <alexander@amelkin.msk.ru>
This commit is contained in:
Alexander Amelkin 2019-04-19 15:07:25 +03:00 committed by Alexander Amelkin
parent d818c2ff85
commit 12e2f5da63
2 changed files with 78 additions and 26 deletions

View File

@ -37,6 +37,7 @@
# include <config.h> # include <config.h>
#endif #endif
#include <stdbool.h>
#include <inttypes.h> #include <inttypes.h>
#include <math.h> #include <math.h>
#include <ipmitool/bswap.h> #include <ipmitool/bswap.h>
@ -381,6 +382,29 @@ struct sdr_record_common_sensor {
struct sdr_record_mask mask; struct sdr_record_mask mask;
/* IPMI 2.0, Table 43-1, byte 21[7:6] Analog (numeric) Data Format */
#define SDR_UNIT_FMT_UNSIGNED 0 /* unsigned */
#define SDR_UNIT_FMT_1S_COMPL 1 /* 1's complement (signed) */
#define SDR_UNIT_FMT_2S_COMPL 2 /* 2's complement (signed) */
#define SDR_UNIT_FMT_NA 3 /* does not return analog (numeric) reading */
/* IPMI 2.0, Table 43-1, byte 21[5:3] Rate */
#define SDR_UNIT_RATE_NONE 0 /* none */
#define SDR_UNIT_RATE_MICROSEC 1 /* per us */
#define SDR_UNIT_RATE_MILLISEC 2 /* per ms */
#define SDR_UNIT_RATE_SEC 3 /* per s */
#define SDR_UNIT_RATE_MIN 4 /* per min */
#define SDR_UNIT_RATE_HR 5 /* per hour */
#define SDR_UNIT_RATE_DAY 6 /* per day */
#define SDR_UNIT_RATE_RSVD 7 /* reserved */
/* IPMI 2.0, Table 43-1, byte 21[2:1] Modifier Unit */
#define SDR_UNIT_MOD_NONE 0 /* none */
#define SDR_UNIT_MOD_DIV 1 /* Basic Unit / Modifier Unit */
#define SDR_UNIT_MOD_MUL 2 /* Basic Unit * Mofifier Unit */
#define SDR_UNIT_MOD_RSVD 3 /* Reserved */
/* IPMI 2.0, Table 43-1, byte 21[0] Percentage */
#define SDR_UNIT_PCT_NO 0
#define SDR_UNIT_PCT_YES 1
struct { struct {
#if WORDS_BIGENDIAN #if WORDS_BIGENDIAN
uint8_t analog:2; uint8_t analog:2;
@ -394,8 +418,8 @@ struct sdr_record_common_sensor {
uint8_t analog:2; uint8_t analog:2;
#endif #endif
struct { struct {
uint8_t base; uint8_t base; /* Base unit type code per IPMI 2.0 Table 43-15 */
uint8_t modifier; uint8_t modifier; /* Modifier unit type code per Table 43-15 */
} ATTRIBUTE_PACKING type; } ATTRIBUTE_PACKING type;
} ATTRIBUTE_PACKING unit; } ATTRIBUTE_PACKING unit;
} ATTRIBUTE_PACKING; } ATTRIBUTE_PACKING;
@ -833,8 +857,8 @@ void ipmi_sdr_print_sensor_hysteresis(struct sdr_record_common_sensor *sensor,
struct sdr_record_full_sensor *full, struct sdr_record_full_sensor *full,
uint8_t hysteresis_value, uint8_t hysteresis_value,
const char *hdrstr); const char *hdrstr);
const char *ipmi_sdr_get_unit_string(uint8_t pct, uint8_t type, const char *ipmi_sdr_get_unit_string(bool pct, uint8_t type,
uint8_t base, uint8_t modifier); uint8_t base, uint8_t modifier);
struct sensor_reading * struct sensor_reading *
ipmi_sdr_read_sensor_value(struct ipmi_intf *intf, ipmi_sdr_read_sensor_value(struct ipmi_intf *intf,
struct sdr_record_common_sensor *sensor, struct sdr_record_common_sensor *sensor,

View File

@ -68,8 +68,9 @@ static struct sdr_record_list *sdr_list_head = NULL;
static struct sdr_record_list *sdr_list_tail = NULL; static struct sdr_record_list *sdr_list_tail = NULL;
static struct ipmi_sdr_iterator *sdr_list_itr = NULL; static struct ipmi_sdr_iterator *sdr_list_itr = NULL;
/* unit description codes (IPMI v1.5 section 37.16) */ /* IPMI 2.0 Table 43-15, Sensor Unit Type Codes */
#define UNIT_MAX 0x90 #define UNIT_TYPE_MAX 92 /* This is the ID of "grams" */
#define UNIT_TYPE_LONGEST_NAME 19 /* This is the length of "color temp deg K" */
static const char *unit_desc[] = { static const char *unit_desc[] = {
"unspecified", "unspecified",
"degrees C", "degrees C",
@ -161,7 +162,9 @@ static const char *unit_desc[] = {
"characters", "characters",
"error", "error",
"correctable error", "correctable error",
"uncorrectable error" "uncorrectable error",
"fatal error",
"grams"
}; };
/* sensor type codes (IPMI v1.5 table 36.3) /* sensor type codes (IPMI v1.5 table 36.3)
@ -220,35 +223,60 @@ void printf_sdr_usage();
uint16_t uint16_t
ipmi_intf_get_max_response_data_size(struct ipmi_intf * intf); ipmi_intf_get_max_response_data_size(struct ipmi_intf * intf);
/* ipmi_sdr_get_unit_string - return units for base/modifier /** ipmi_sdr_get_unit_string - return units for base/modifier
* *
* @pct: units are a percentage * @param[in] pct Indicates that units are a percentage
* @type: unit type * @param[in] relation Modifier unit to base unit relation
* @base: base * (SDR_UNIT_MOD_NONE, SDR_UNIT_MOD_MUL,
* @modifier: modifier * or SDR_UNIT_MOD_DIV)
* @param[in] base The base unit type id
* @param[in] modifier The modifier unit type id
* *
* returns pointer to static string * @returns a pointer to static string
*/ */
const char * const char *
ipmi_sdr_get_unit_string(uint8_t pct, uint8_t type, uint8_t base, uint8_t modifier) ipmi_sdr_get_unit_string(bool pct, uint8_t relation,
uint8_t base, uint8_t modifier)
{ {
static char unitstr[16]; /*
* Twice as long as the longest possible unit name, plus
* two characters for '%' and relation (either '*' or '/'),
* plus the terminating null-byte.
*/
static char unitstr[2 * UNIT_TYPE_LONGEST_NAME + 2 + 1];
/* /*
* By default, if units are supposed to be percent, we will pre-pend * By default, if units are supposed to be percent, we will pre-pend
* the percent string to the textual representation of the units. * the percent string to the textual representation of the units.
*/ */
char *pctstr = pct ? "% " : ""; const char *pctstr = pct ? "% " : "";
memset(unitstr, 0, sizeof (unitstr)); const char *basestr;
switch (type) { const char *modstr;
case 2:
snprintf(unitstr, sizeof (unitstr), "%s%s * %s", if (base <= UNIT_TYPE_MAX) {
pctstr, unit_desc[base], unit_desc[modifier]); basestr = unit_desc[base];
}
else {
basestr = "invalid";
}
if (modifier <= UNIT_TYPE_MAX) {
modstr = unit_desc[base];
}
else {
modstr = "invalid";
}
switch (relation) {
case SDR_UNIT_MOD_MUL:
snprintf(unitstr, sizeof (unitstr), "%s%s*%s",
pctstr, basestr, modstr);
break; break;
case 1: case SDR_UNIT_MOD_DIV:
snprintf(unitstr, sizeof (unitstr), "%s%s/%s", snprintf(unitstr, sizeof (unitstr), "%s%s/%s",
pctstr, unit_desc[base], unit_desc[modifier]); pctstr, basestr, modstr);
break; break;
case 0: case SDR_UNIT_MOD_NONE:
default: default:
/* /*
* Display the text "percent" only when the Base unit is * Display the text "percent" only when the Base unit is
@ -258,7 +286,7 @@ ipmi_sdr_get_unit_string(uint8_t pct, uint8_t type, uint8_t base, uint8_t modifi
snprintf(unitstr, sizeof(unitstr), "percent"); snprintf(unitstr, sizeof(unitstr), "percent");
} else { } else {
snprintf(unitstr, sizeof (unitstr), "%s%s", snprintf(unitstr, sizeof (unitstr), "%s%s",
pctstr, unit_desc[base]); pctstr, basestr);
} }
break; break;
} }