aarch64: Don't assert on long sysreg names

This patch fixes an assertion failure on long system register operands
in the AArch64 backend. See the new testcase for an input which
reproduces the issue.

gas/ChangeLog:

	* config/tc-aarch64.c (parse_sys_reg): Don't assert when parsing
	a long system register.
	(parse_sys_ins_reg): Likewise.
	(sysreg_hash_insert): New.
	(md_begin): Use sysreg_hash_insert() to ensure all system
	registers are no longer than the maximum length at startup.
	* testsuite/gas/aarch64/invalid-sysreg-assert.d: New test.
	* testsuite/gas/aarch64/invalid-sysreg-assert.l: Error output.
	* testsuite/gas/aarch64/invalid-sysreg-assert.s: Input.

include/ChangeLog:

	* opcode/aarch64.h (AARCH64_MAX_SYSREG_NAME_LEN): New.
This commit is contained in:
Alex Coplan
2020-08-10 17:44:02 +01:00
parent 9546e03d55
commit fa63795f40
7 changed files with 55 additions and 13 deletions

View File

@ -1,3 +1,15 @@
2020-08-10 Alex Coplan <alex.coplan@arm.com>
* config/tc-aarch64.c (parse_sys_reg): Don't assert when parsing
a long system register.
(parse_sys_ins_reg): Likewise.
(sysreg_hash_insert): New.
(md_begin): Use sysreg_hash_insert() to ensure all system
registers are no longer than the maximum length at startup.
* testsuite/gas/aarch64/invalid-sysreg-assert.d: New test.
* testsuite/gas/aarch64/invalid-sysreg-assert.l: Error output.
* testsuite/gas/aarch64/invalid-sysreg-assert.s: Input.
2020-08-10 Przemyslaw Wirkus <przemyslaw.wirkus@arm.com> 2020-08-10 Przemyslaw Wirkus <przemyslaw.wirkus@arm.com>
* config/tc-aarch64.c (parse_sys_reg): Call to * config/tc-aarch64.c (parse_sys_reg): Call to

View File

@ -4100,17 +4100,21 @@ parse_sys_reg (char **str, struct hash_control *sys_regs,
uint32_t* flags) uint32_t* flags)
{ {
char *p, *q; char *p, *q;
char buf[32]; char buf[AARCH64_MAX_SYSREG_NAME_LEN];
const aarch64_sys_reg *o; const aarch64_sys_reg *o;
int value; int value;
p = buf; p = buf;
for (q = *str; ISALNUM (*q) || *q == '_'; q++) for (q = *str; ISALNUM (*q) || *q == '_'; q++)
if (p < buf + 31) if (p < buf + (sizeof (buf) - 1))
*p++ = TOLOWER (*q); *p++ = TOLOWER (*q);
*p = '\0'; *p = '\0';
/* Assert that BUF be large enough. */
gas_assert (p - buf == q - *str); /* If the name is longer than AARCH64_MAX_SYSREG_NAME_LEN then it cannot be a
valid system register. This is enforced by construction of the hash
table. */
if (p - buf != q - *str)
return PARSE_FAIL;
o = hash_find (sys_regs, buf); o = hash_find (sys_regs, buf);
if (!o) if (!o)
@ -4161,15 +4165,21 @@ static const aarch64_sys_ins_reg *
parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs) parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs)
{ {
char *p, *q; char *p, *q;
char buf[32]; char buf[AARCH64_MAX_SYSREG_NAME_LEN];
const aarch64_sys_ins_reg *o; const aarch64_sys_ins_reg *o;
p = buf; p = buf;
for (q = *str; ISALNUM (*q) || *q == '_'; q++) for (q = *str; ISALNUM (*q) || *q == '_'; q++)
if (p < buf + 31) if (p < buf + (sizeof (buf) - 1))
*p++ = TOLOWER (*q); *p++ = TOLOWER (*q);
*p = '\0'; *p = '\0';
/* If the name is longer than AARCH64_MAX_SYSREG_NAME_LEN then it cannot be a
valid system register. This is enforced by construction of the hash
table. */
if (p - buf != q - *str)
return NULL;
o = hash_find (sys_ins_regs, buf); o = hash_find (sys_ins_regs, buf);
if (!o) if (!o)
return NULL; return NULL;
@ -8620,6 +8630,13 @@ checked_hash_insert (struct hash_control *table, const char *key, void *value)
printf ("Internal Error: Can't hash %s\n", key); printf ("Internal Error: Can't hash %s\n", key);
} }
static void
sysreg_hash_insert (struct hash_control *table, const char *key, void *value)
{
gas_assert (strlen (key) < AARCH64_MAX_SYSREG_NAME_LEN);
checked_hash_insert (table, key, value);
}
static void static void
fill_instruction_hash_table (void) fill_instruction_hash_table (void)
{ {
@ -8694,36 +8711,36 @@ md_begin (void)
fill_instruction_hash_table (); fill_instruction_hash_table ();
for (i = 0; aarch64_sys_regs[i].name != NULL; ++i) for (i = 0; aarch64_sys_regs[i].name != NULL; ++i)
checked_hash_insert (aarch64_sys_regs_hsh, aarch64_sys_regs[i].name, sysreg_hash_insert (aarch64_sys_regs_hsh, aarch64_sys_regs[i].name,
(void *) (aarch64_sys_regs + i)); (void *) (aarch64_sys_regs + i));
for (i = 0; aarch64_pstatefields[i].name != NULL; ++i) for (i = 0; aarch64_pstatefields[i].name != NULL; ++i)
checked_hash_insert (aarch64_pstatefield_hsh, sysreg_hash_insert (aarch64_pstatefield_hsh,
aarch64_pstatefields[i].name, aarch64_pstatefields[i].name,
(void *) (aarch64_pstatefields + i)); (void *) (aarch64_pstatefields + i));
for (i = 0; aarch64_sys_regs_ic[i].name != NULL; i++) for (i = 0; aarch64_sys_regs_ic[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_ic_hsh, sysreg_hash_insert (aarch64_sys_regs_ic_hsh,
aarch64_sys_regs_ic[i].name, aarch64_sys_regs_ic[i].name,
(void *) (aarch64_sys_regs_ic + i)); (void *) (aarch64_sys_regs_ic + i));
for (i = 0; aarch64_sys_regs_dc[i].name != NULL; i++) for (i = 0; aarch64_sys_regs_dc[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_dc_hsh, sysreg_hash_insert (aarch64_sys_regs_dc_hsh,
aarch64_sys_regs_dc[i].name, aarch64_sys_regs_dc[i].name,
(void *) (aarch64_sys_regs_dc + i)); (void *) (aarch64_sys_regs_dc + i));
for (i = 0; aarch64_sys_regs_at[i].name != NULL; i++) for (i = 0; aarch64_sys_regs_at[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_at_hsh, sysreg_hash_insert (aarch64_sys_regs_at_hsh,
aarch64_sys_regs_at[i].name, aarch64_sys_regs_at[i].name,
(void *) (aarch64_sys_regs_at + i)); (void *) (aarch64_sys_regs_at + i));
for (i = 0; aarch64_sys_regs_tlbi[i].name != NULL; i++) for (i = 0; aarch64_sys_regs_tlbi[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_tlbi_hsh, sysreg_hash_insert (aarch64_sys_regs_tlbi_hsh,
aarch64_sys_regs_tlbi[i].name, aarch64_sys_regs_tlbi[i].name,
(void *) (aarch64_sys_regs_tlbi + i)); (void *) (aarch64_sys_regs_tlbi + i));
for (i = 0; aarch64_sys_regs_sr[i].name != NULL; i++) for (i = 0; aarch64_sys_regs_sr[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_sr_hsh, sysreg_hash_insert (aarch64_sys_regs_sr_hsh,
aarch64_sys_regs_sr[i].name, aarch64_sys_regs_sr[i].name,
(void *) (aarch64_sys_regs_sr + i)); (void *) (aarch64_sys_regs_sr + i));

View File

@ -0,0 +1,3 @@
#name: don't assert on long system register
#source: invalid-sysreg-assert.s
#error_output: invalid-sysreg-assert.l

View File

@ -0,0 +1,2 @@
[^:]*: Assembler messages:
.*: Error: unknown or missing system register name at operand 1 -- `msr 00000000000000000000000000000000'

View File

@ -0,0 +1,2 @@
// This input caused an assertion failure in parse_sys_reg.
msr 00000000000000000000000000000000

View File

@ -1,3 +1,7 @@
2020-08-10 Alex Coplan <alex.coplan@arm.com>
* opcode/aarch64.h (AARCH64_MAX_SYSREG_NAME_LEN): New.
2020-08-10 Przemyslaw Wirkus <przemyslaw.wirkus@arm.com> 2020-08-10 Przemyslaw Wirkus <przemyslaw.wirkus@arm.com>
* opcode/aarch64.h (aarch64_sys_reg_deprecated_p): Functions * opcode/aarch64.h (aarch64_sys_reg_deprecated_p): Functions

View File

@ -943,6 +943,8 @@ extern const struct aarch64_name_value_pair aarch64_barrier_options [16];
extern const struct aarch64_name_value_pair aarch64_prfops [32]; extern const struct aarch64_name_value_pair aarch64_prfops [32];
extern const struct aarch64_name_value_pair aarch64_hint_options []; extern const struct aarch64_name_value_pair aarch64_hint_options [];
#define AARCH64_MAX_SYSREG_NAME_LEN 32
typedef struct typedef struct
{ {
const char * name; const char * name;