Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a crash when running 'unalias r' #133

Merged
merged 4 commits into from
Sep 18, 2020
Merged

Conversation

JohnoKing
Copy link

After commit ddaa145, the following set of commands now causes ksh to crash:

$ unalias history; unalias r
Memory fault

When ksh is compiled with -D_std_malloc, the crash always occurs when the r alias is removed with unalias r, although with vmalloc unalias history must be run first for the crash to occur. The crash message is also different when the native malloc is used instead of vmalloc:

$ unalias r
free(): invalid pointer
Abort

This crash happens because the NV_NOFREE flag disappears after _nv_unset, which results in nv_isattr becoming a no-op (as it always returns zero). nv_delete then attempts an invalid free, which causes the crash:

if(!nv_isnull(np) || nv_size(np) || nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)))
_nv_unset(np,0);

nv_delete(np,troot,nv_isattr(np,NV_NOFREE));

The history and r aliases shouldn't be freed from memory by nv_delete because those aliases are given the NV_NOFREE flag:

"history", NV_NOFREE, "hist -l",
"r", NV_NOFREE, "hist -s",

This pull request fixes the crash by saving the NV_NOFREE flag for aliases to prevent an invalid free from occurring.

After commit ddaa145, the following set of commands now
causes ksh to crash:

$ unalias history; unalias r
Memory fault

When ksh is compiled with -D_std_malloc, the crash always
occurs when the 'r' alias is removed with 'unalias r',
although with vmalloc 'unalias history' must be run first
for the crash to occur. With the native malloc, the crash
message is also different:

$ unalias history; unalias r
free(): invalid pointer
Abort

This crash happens because when an alias is unset, _nv_unset
removes the NV_NOFREE flag which results in an invalid use
of free(3) as nv_isattr no longer detects NV_NOFREE afterward.
The history and r aliases shouldn't be freed from memory by
nv_delete because those aliases are given the NV_NOFREE attribute.

src/cmd/ksh93/bltins/typeset.c:
- Save the state of NV_NOFREE for aliases to fix the crash
  caused by 'unalias r'. Also of note, using the return
  value from nv_isattr is incorrect since it's just a
  boolean value. The actual flag passed must be NV_NOFREE
  to prevent bugs (like the crash).

src/cmd/ksh93/tests/alias.sh:
- Use unalias on both history and r to check for the crash.
  'unalias -a' can't be used to replicate the crash.
@JohnoKing JohnoKing changed the title Fix a memory fault caused by 'unalias r' Fix a crash when running 'unalias r' Sep 15, 2020
@McDutchie
Copy link

Looks like I done goofed again. Thank you for this fix!

@McDutchie
Copy link

McDutchie commented Sep 15, 2020

I'm still going to give this some more thought before applying it, though. Maybe the real bug is that _nv_unset() discards a node's NV_NOFREE flag. Why on earth would it do such a thing? NV_NOFREE signifies that the node is in read-only memory, so that flag should never be unset.

I'm going to be busy with other things in the next few days, so this will have to wait for a while. Meanwhile if you have any thoughts on that I'd appreciate them.

@McDutchie
Copy link

Well, this diff

diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c
index 2a0c216..d5d5276 100644
--- a/src/cmd/ksh93/sh/name.c
+++ b/src/cmd/ksh93/sh/name.c
@@ -2536,7 +2536,7 @@ done:
 				env_delete(shp->env,nv_name(np));
 			if(!(flags&NV_EXPORT) ||  nv_isattr(np,NV_EXPORT))
 				np->nvenv = 0;
-			nv_setattr(np,0);
+			nv_setattr(np,nv_isattr(np,NV_NOFREE));
 		}
 		else
 		{

fixes the crash, but also causes multiple regressions:

	arrays.sh[575]: typeset foo[7] should not have one element
	attributes.sh[447]: typeset -pa does not list only index arrays
	cubetype.sh[193]: ${#cc[@]} != 2
	cubetype.sh[209]: ${#cc[@]} failed -- expected '3', got '4'
	cubetype.sh[153]: cc[0].x !=8
	cubetype.sh[163]: cc[2].len != cc[0].len
	cubetype.sh[165]: cc[2].count != 6
	cubetype.sh[173]: ${#cc[@]} != 2
	cubetype.sh[193]: ${#cc[@]} != 2
	cubetype.sh[209]: ${#cc[@]} failed -- expected '3', got '4'
	types.sh[358]: compound variable with array of types with no elements not working

So, I'll merge your fix then.

Since aliases don't have virtual subshell tables, no need to
_nv_unset() them first.
@McDutchie
Copy link

McDutchie commented Sep 18, 2020

Actually, I think your fix can be made more concise. As of ec88886, aliases don't have virtual subshell tables. So there's no need to mark an alias node as unset first with _nv_unset(); we can simply nv_delete() it directly.

I've pushed a commit with that change to your fork. Since this is your pull request, please test it and let me know if you give it the OK.

@McDutchie
Copy link

Actually, never mind. My version introduces a memory leak. Apparently, we must _nv_unset() before calling nv_delete(). Which makes sense because nv_delete() only frees the node itself, _nv_unset() is what frees the value.

Actually, never mind. My version introduces a memory leak.
Apparently, we must _nv_unset() before calling nv_delete(). Which
makes sense because nv_delete() only frees the node itself,
_nv_unset() is what frees the value.

This reverts commit f6ddacf.
@McDutchie
Copy link

McDutchie commented Sep 18, 2020

You're definitely not correct about one thing, though: nv_isattr is not a boolean value, it returns a value with the bit corresponding to the flag if that flag is set. The macro is defined as follows:

#define nv_isattr(np,f) ((np)->nvflag & (f))
Note it uses a bitwise AND (&) and not a logical AND (&&). So you could even use this to test for multiple flags at the same time, as in nv_isattr(np,FLAG1|FLAG2|FLAG3) and it will return the flags that are set.

@McDutchie McDutchie merged commit 7e7f137 into ksh93:master Sep 18, 2020
@JohnoKing JohnoKing deleted the nofree-fix branch September 18, 2020 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants