Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Segmentation fault immediately after 'unset PATH'

hu, 2019-07-11 at 09:44 +0100, Peter Stephenson wrote:
 Wed, 2019-07-10 at 23:52 +0200, Mikael Magnusson wrote:

On 7/10/19, Shane Squires <shane2squires@xxxxxxxxx> wrote:
> The following script, when executed, produces a segmentation fault for me.
> This is the most minimal example I can construct.
> ------------------
> File run.zsh:
> ------------------
> #! /usr/bin/zsh
> run() {
>   typeset -U path=( $path )
>   unset PATH
> }
> run

I think the issue is that to be able to save and restore path, which is
special, we stick the old values on the end but keep the special
parameter structure in front.  The value to be saved for the environment
is associated with path, so when we try to unset that we throw away the
real special parameter, which we should simply mark as unset.

It's straightforward to fix the actual crash.

The other business here is that the "unset" unsets the global PATH, as
there isn't a local parameter, just the local path which refers to the
same data but has a separate parameter interface.  So PATH remains unset
until you explicitly set it again.

It's deliberate that you can unset one of the variables and still leave
the other visible.  Given that, forcing them both to be local at the
same time is a bit murky, and I can more or less guarantee a bug-prone
mess if we try.


diff --git a/Src/params.c b/Src/params.c
index 1859c7c12..95181f533 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -3617,10 +3617,18 @@ unsetparam_pm(Param pm, int altflag, int exp)
 	altpm = (Param) paramtab->getnode(paramtab, altremove);
 	/* tied parameters are at the same local level as each other */
 	oldpm = NULL;
-	while (altpm && altpm->level > pm->level) {
-	    /* param under alternate name hidden by a local */
-	    oldpm = altpm;
-	    altpm = altpm->old;
+	/*
+	 * Look for param under alternate name hidden by a local.
+	 * If this parameter is special, however, the visible
+	 * parameter is the special and the hidden one is keeping
+	 * and old value --- we just mark the visible one as unset.
+	 */
+	if (altpm && !(altpm->node.flags & PM_SPECIAL))
+	{
+	    while (altpm && altpm->level > pm->level) {
+		oldpm = altpm;
+		altpm = altpm->old;
+	    }
 	if (altpm) {
 	    if (oldpm && !altpm->level) {

Messages sorted by: Reverse Date, Date, Thread, Author