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

Declaration command names break 2D indexed array assignments #792

Open
McDutchie opened this issue Nov 5, 2024 · 7 comments
Open

Declaration command names break 2D indexed array assignments #792

McDutchie opened this issue Nov 5, 2024 · 7 comments
Labels
bug Something is not working

Comments

@McDutchie
Copy link

McDutchie commented Nov 5, 2024

A declaration command name like export or typeset breaks multidimensional indexed array assignments, e.g.:

$ typeset -a arr=( (a b c) (export demo array) )
$ typeset -p arr
typeset -a arr=((a b c) ())

Expected output:

typeset -a arr=((a b c) (export demo array) )

Originally reported by @TristanJamesBall in #790 (comment)

@McDutchie McDutchie added the bug Something is not working label Nov 5, 2024
@McDutchie
Copy link
Author

McDutchie commented Nov 5, 2024

This bug has been in ksh since forever as well. It also happens when typeset or other declaration command names are used in place of export.

@McDutchie
Copy link
Author

McDutchie commented Nov 5, 2024

The problem is that export demo array is executed as a command instead of those words being taken as values for array members.

The export command is handled by b_readonly() in typeset.c. When we make that abort, this call stack results. It might help us find where things go wrong.

diff --git a/src/cmd/ksh93/bltins/typeset.c b/src/cmd/ksh93/bltins/typeset.c
index 64880f865..d66a97c61 100644
--- a/src/cmd/ksh93/bltins/typeset.c
+++ b/src/cmd/ksh93/bltins/typeset.c
@@ -90,6 +90,7 @@ static void(*nullscan)(Namval_t*,void*);
 #endif
 int    b_readonly(int argc,char *argv[],Shbltin_t *context)
 {
+abort();
 	int flag;
 	char *command = argv[0];
 	struct tdata tdata;
$ lldb -- arch/darwin.arm64-64/bin/ksh -c 'typeset -a arr=( (a b c) (export demo array) )'
(lldb) target create "arch/darwin.arm64-64/bin/ksh"
Current executable set to '/usr/local/src/ksh93/ksh/arch/darwin.arm64-64/bin/ksh' (arm64).
(lldb) settings set -- target.run-args  "-c" "typeset -a arr=( (a b c) (export demo array) )"
(lldb) run
Process 57938 launched: '/usr/local/src/ksh93/ksh/arch/darwin.arm64-64/bin/ksh' (arm64)
Process 57938 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00000001a934ed38 libsystem_kernel.dylib`__pthread_kill + 8
libsystem_kernel.dylib`:
->  0x1a934ed38 <+8>:  b.lo   0x1a934ed58               ; <+40>
    0x1a934ed3c <+12>: pacibsp 
    0x1a934ed40 <+16>: stp    x29, x30, [sp, #-0x10]!
    0x1a934ed44 <+20>: mov    x29, sp
Target 0: (ksh) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00000001a934ed38 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x00000001a9383ee0 libsystem_pthread.dylib`pthread_kill + 288
    frame #2: 0x00000001a92be330 libsystem_c.dylib`abort + 168
    frame #3: 0x00000001000178c0 ksh`b_readonly(argc=3, argv=0x000000010080b2e0, context=0x0000000100196210) at typeset.c:93:1
    frame #4: 0x0000000100094b28 ksh`sh_exec(t=0x000000010080b250, flags=4) at xec.c:1271:21
    frame #5: 0x000000010006cb08 ksh`nv_setlist(arg=0x000000010080b1f8, flags=8520192, typ=0x0000000000000000) at name.c:564:5
    frame #6: 0x000000010006c4f0 ksh`nv_setlist(arg=0x000000010080b098, flags=8520192, typ=0x0000000000000000) at name.c:491:7
    frame #7: 0x0000000100093eac ksh`sh_exec(t=0x000000010080b040, flags=5) at xec.c:1077:7
    frame #8: 0x0000000100020d90 ksh`exfile(iop=0x0000600003500420, fno=-1) at main.c:595:4
    frame #9: 0x000000010001fe98 ksh`sh_main(ac=3, av=0x000000016fdff678, userinit=0x0000000000000000) at main.c:348:2
    frame #10: 0x000000010000464c ksh`main(argc=3, argv=0x000000016fdff678) at pmain.c:41:9
    frame #11: 0x00000001001e108c dyld`start + 520
(lldb) 

@McDutchie
Copy link
Author

McDutchie commented Nov 5, 2024

So, the first thing that stands out is that nv_setlist calls nv_setlist, see frames 6 and 5. On line 491, flags is passed on to the recursive call, minus any NV_STATIC bit.

However, on line 262, the NV_IARRAY bit, which indicates an indexed array, is cleared from flags, after having been saved in the array variable on line 230. This means the recursive call on line 491 is not passing on the NV_IARRAY bit, so the nested parentheses are not (properly) treated as containing indexed array values. This then incorrectly allows declaration commands to be executed, as if we were dealing with a compound assignment.

This patch should fix that, and (in my testing) fixes the reproducer:

diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c
index 25d383b87..72b42cbbc 100644
--- a/src/cmd/ksh93/sh/name.c
+++ b/src/cmd/ksh93/sh/name.c
@@ -488,7 +488,7 @@ void nv_setlist(struct argnod *arg,int flags, Namval_t *typ)
 							if(!(array&NV_IARRAY) && !(tp->com.comset->argflag&ARG_MESSAGE))
 								nv_setarray(np,nv_associative);
 						}
-						nv_setlist(tp->com.comset,flags&~NV_STATIC,0);
+						nv_setlist(tp->com.comset, flags & ~NV_STATIC | array & NV_IARRAY, 0);
 						sh.prefix = prefix;
 						if(tp->com.comset->argval[1]!='[')
 							 nv_setvtree(np);

@McDutchie
Copy link
Author

Unfortunately, while the patch fixes the reproducer, it also causes regressions in compound variable handling.

test comvar begins at 2024-11-05+20:35:13
	comvar.sh[542]: FAIL: typeset -p with nested compound indexed array not working
/usr/local/src/ksh93/ksh/src/cmd/ksh93/tests/comvar.sh[587]: eval: syntax error at line 1: `)' unexpected
	comvar.sh[588]: FAIL: print -C with multidimensional array not working
/usr/local/src/ksh93/ksh/src/cmd/ksh93/tests/comvar.sh[589]: eval: syntax error at line 2: `)' unexpected
	comvar.sh[590]: FAIL: print -v with multidimensional array not working
	comvar.sh[599]: FAIL: typeset -m not working with compound -a variable
	comvar.sh[631]: FAIL: moving compound indexed array element to a compound associative array element fails
test comvar failed at 2024-11-05+20:35:13 with exit code 5 [ 104 tests 5 errors ]
test comvario begins at 2024-11-05+20:35:13
/usr/local/src/ksh93/ksh/src/cmd/ksh93/tests/comvario.sh[407]: test6[398]: read: syntax error at line 409: `)' unexpected
	comvario.sh[398]: FAIL: test2/18: read -C val failed with exit code 3
/usr/local/src/ksh93/ksh/src/cmd/ksh93/tests/comvario.sh[407]: test6: line 400: aa: parameter not set
test comvario failed at 2024-11-05+20:35:15 with exit code 1 [ 72 tests 1 error ]

@McDutchie
Copy link
Author

McDutchie commented Nov 5, 2024

Thankfully, compound variable assignments are conveniently flagged with the NV_COMVAR bit. So we need to avoid passing the NV_IARRAY flag if the indexed array is part of a compound assignment. Patch version two passes all the current regression tests, but we should try testing it for any bugs that might not be caught by current tests.

diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c
index 25d383b87..ceaad5161 100644
--- a/src/cmd/ksh93/sh/name.c
+++ b/src/cmd/ksh93/sh/name.c
@@ -478,6 +478,7 @@ void nv_setlist(struct argnod *arg,int flags, Namval_t *typ)
 				{
 					if(tp->tre.tretyp!=TLST && !tp->com.comnamp && tp->com.comset && tp->com.comset->argval[0]==0 && tp->com.comset->argchn.ap)
 					{
+						int multidim_iarray;
 						if(prefix || np)
 							cp = stkcopy(sh.stk,nv_name(np));
 						sh.prefix = cp;
@@ -488,7 +489,8 @@ void nv_setlist(struct argnod *arg,int flags, Namval_t *typ)
 							if(!(array&NV_IARRAY) && !(tp->com.comset->argflag&ARG_MESSAGE))
 								nv_setarray(np,nv_associative);
 						}
-						nv_setlist(tp->com.comset,flags&~NV_STATIC,0);
+						multidim_iarray = (flags & NV_COMVAR) ? 0 : (array & NV_IARRAY);
+						nv_setlist(tp->com.comset, flags & ~NV_STATIC | multidim_iarray, 0);
 						sh.prefix = prefix;
 						if(tp->com.comset->argval[1]!='[')
 							 nv_setvtree(np);

@McDutchie
Copy link
Author

Sigh. Unfortunately, the fix is still incomplete at best:

$ arch/*/bin/ksh -c 'typeset -a arr=( (a (export b) c) (export demo array) ); typeset -p arr'  
typeset -a arr=((a () c) (export demo array) )

Pre-patch output:

typeset -a arr=(([0]=a [1]=()) ())

Expected output:

typeset -a arr=( (a (export b) c) (export demo array) )

@McDutchie
Copy link
Author

Repeating the abort() trick with the latest reproducer, we get the following call trace:

  * frame #0: 0x00000001a934ed38 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x00000001a9383ee0 libsystem_pthread.dylib`pthread_kill + 288
    frame #2: 0x00000001a92be330 libsystem_c.dylib`abort + 168
    frame #3: 0x0000000100017888 ksh`b_readonly(argc=2, argv=0x00000001010098b0, context=0x0000000100196210) at typeset.c:93:1
    frame #4: 0x0000000100094b28 ksh`sh_exec(t=0x0000000101009838, flags=4) at xec.c:1271:21
    frame #5: 0x000000010006cb08 ksh`nv_setlist(arg=0x00000001010097e0, flags=131584, typ=0x0000000000000000) at name.c:566:5
    frame #6: 0x0000000100093eac ksh`sh_exec(t=0x0000000101009788, flags=4) at xec.c:1077:7
    frame #7: 0x000000010006cb08 ksh`nv_setlist(arg=0x0000000101009730, flags=8520192, typ=0x0000000000000000) at name.c:566:5
    frame #8: 0x000000010006c4f0 ksh`nv_setlist(arg=0x0000000101009698, flags=8520192, typ=0x0000000000000000) at name.c:493:7
    frame #9: 0x0000000100093eac ksh`sh_exec(t=0x0000000101009640, flags=4) at xec.c:1077:7
    frame #10: 0x0000000100096abc ksh`sh_exec(t=0x0000000101009b10, flags=5) at xec.c:1954:5
    frame #11: 0x0000000100020d58 ksh`exfile(iop=0x0000600003504370, fno=-1) at main.c:595:4
    frame #12: 0x000000010001fe60 ksh`sh_main(ac=3, av=0x000000016fdff660, userinit=0x0000000000000000) at main.c:348:2
    frame #13: 0x0000000100004614 ksh`main(argc=3, argv=0x000000016fdff660) at pmain.c:41:9
    frame #14: 0x00000001001e108c dyld`start + 520

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is not working
Projects
None yet
Development

No branches or pull requests

1 participant