-
Notifications
You must be signed in to change notification settings - Fork 32
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
Memory leak when initialising associative array in subshell #94
Comments
If you unset the array within the subshell after setting it, then the memory leak does not disappear as expected, but gets about three times as large! Very odd. for ((i=1; i<=10; i++)); do
for ((n=1; n<=100000; n++)); do
(typeset -A foo=([a]=1 [b]=2 [c]=3); unset foo)
done
ps -p "$$" -o rss=,vsz=
done Output on macOS:
|
Just from the comments here I would suspect the leak stems from allocating tracking records for the index labels 'a', 'b', and 'c' in the parent context, not the subshell context, that are orphaned from association with "foo" on subshell exit, preventing cleanup "unset foo" might invoke when the array is declared directly, i.e. in { } pair or inline, not ( ) one. |
edit: I figured out the cause of the size difference; it's that I'm no longer compiling ksh with vmalloc. So it leaks more with vmalloc than with the OS's malloc. This is not a surprise as vmalloc was already known to be wasteful. Unfortunately nothing changed about the actual leak. This memory leak on macOS is currently about a third of the size it used to be:
It still grows a lot when adding an
|
If no real fix can be found, the usual forking workaround is effective. And this is not a performance-sensitive thing; only regression tests declare associative arrays thousands of times in a loop. So I might just end up committing this. The forking needs to be done in sh_exec() at a stage before --- a/src/cmd/ksh93/sh/xec.c
+++ b/src/cmd/ksh93/sh/xec.c
@@ -1062,7 +1062,11 @@ int sh_exec(register const Shnode_t *t, int flags)
if(argn)
{
if(checkopt(com,'A'))
+ {
+ if(sh.subshell && !sh.subshare)
+ sh_subfork();
flgs |= NV_ARRAY;
+ }
else if(checkopt(com,'a'))
flgs |= NV_IARRAY;
} |
For what it's worth this is what ASan outputs when running the reproducer with an added $ arch/*/bin/ksh /tmp/foo
181848 21474914836
350784 21474914836
519700 21474914836
639256 21474914836
681560 21474914836
723432 21474914836
765280 21474914836
788308 21474914836
850388 21474914836
892244 21474914836
=================================================================
==495815==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 173999942 byte(s) in 2999999 object(s) allocated from:
#0 0x7f10c9851459 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:154
#1 0x55c93816a0c7 in sh_calloc /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/init.c:248
#2 0x55c93812a656 in newnode /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/nvdisc.c:862
#3 0x55c93812c18b in nv_search /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/nvdisc.c:1099
#4 0x55c938150f56 in nv_associative /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/array.c:1770
#5 0x55c93814d41d in nv_putsub /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/array.c:1332
#6 0x55c93814f452 in nv_endsubscript /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/array.c:1552
#7 0x55c9381d0062 in nv_create /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:1068
#8 0x55c9381d2c88 in nv_open /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:1433
#9 0x55c9381cc2f3 in nv_setlist /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:610
#10 0x55c93822a20d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1143
#11 0x55c9381cbafb in nv_setlist /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:563
#12 0x55c93822a20d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1143
#13 0x55c9382340c0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2080
#14 0x55c93821a524 in sh_subshell /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/subshell.c:673
#15 0x55c938233007 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1961
#16 0x55c9382361b0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2273
#17 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#18 0x55c9382340c0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2080
#19 0x55c9382361b0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2273
#20 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#21 0x55c938122736 in exfile /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:602
#22 0x55c93811ff0c in sh_main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:363
#23 0x55c93811dffd in main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/pmain.c:45
#24 0x7f10c9498b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
Direct leak of 6000000 byte(s) in 3000000 object(s) allocated from:
#0 0x7f10c9851279 in __interceptor_malloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:145
#1 0x55c93816a041 in sh_malloc /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/init.c:232
#2 0x55c9381d6dee in nv_putval /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:2009
#3 0x55c93812495f in nv_putv /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/nvdisc.c:151
#4 0x55c938148342 in array_putval /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/array.c:703
#5 0x55c938124902 in nv_putv /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/nvdisc.c:146
#6 0x55c9381d43ab in nv_putval /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:1634
#7 0x55c9381d3bc7 in nv_open /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:1552
#8 0x55c9381cc2f3 in nv_setlist /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:610
#9 0x55c93822a20d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1143
#10 0x55c9381cbafb in nv_setlist /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/name.c:563
#11 0x55c93822a20d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1143
#12 0x55c9382340c0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2080
#13 0x55c93821a524 in sh_subshell /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/subshell.c:673
#14 0x55c938233007 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1961
#15 0x55c9382361b0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2273
#16 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#17 0x55c9382340c0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2080
#18 0x55c9382361b0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2273
#19 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#20 0x55c938122736 in exfile /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:602
#21 0x55c93811ff0c in sh_main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:363
#22 0x55c93811dffd in main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/pmain.c:45
#23 0x7f10c9498b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
Direct leak of 93 byte(s) in 1 object(s) allocated from:
#0 0x7f10c9851459 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:154
#1 0x55c93816a0c7 in sh_calloc /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/init.c:248
#2 0x55c938203c45 in path_addcomp /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:1535
#3 0x55c938204fdd in path_addpath /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:1674
#4 0x55c9381fc567 in defpath_init /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:439
#5 0x55c9381f9f0e in onstdpath /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:66
#6 0x55c9381fb8c5 in path_checkdup /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:362
#7 0x55c9381fbde1 in path_nextcomp /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:400
#8 0x55c9381fec53 in path_absolute /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:783
#9 0x55c9381fe6dc in path_search /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:725
#10 0x55c93822b035 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1230
#11 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#12 0x55c9382361b0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2273
#13 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#14 0x55c938122736 in exfile /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:602
#15 0x55c93811ff0c in sh_main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:363
#16 0x55c93811dffd in main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/pmain.c:45
#17 0x7f10c9498b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
Indirect leak of 97 byte(s) in 1 object(s) allocated from:
#0 0x7f10c9851459 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:154
#1 0x55c93816a0c7 in sh_calloc /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/init.c:248
#2 0x55c938203c45 in path_addcomp /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:1535
#3 0x55c938204fdd in path_addpath /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:1674
#4 0x55c9381fc567 in defpath_init /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:439
#5 0x55c9381f9f0e in onstdpath /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:66
#6 0x55c9381fb8c5 in path_checkdup /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:362
#7 0x55c9381fbde1 in path_nextcomp /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:400
#8 0x55c9381fec53 in path_absolute /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:783
#9 0x55c9381fe6dc in path_search /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:725
#10 0x55c93822b035 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:1230
#11 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#12 0x55c9382361b0 in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2273
#13 0x55c93823413d in sh_exec /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/xec.c:2084
#14 0x55c938122736 in exfile /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:602
#15 0x55c93811ff0c in sh_main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/main.c:363
#16 0x55c93811dffd in main /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/pmain.c:45
#17 0x7f10c9498b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
SUMMARY: AddressSanitizer: 180000132 byte(s) leaked in 6000001 allocation(s). |
@McDutchie Not sure if I understand completely the impact of your potential patch at #94 (comment) would have on ksh's behavior and performance. Would this patch introduce a difference in what kind of subshell [ forked/non-forked, {}/() , subshare/subfork ] is created when using an associative array? Perhaps it is just removing some of ksh's optimization logic to not create a new fork as when the
|
@McDutchie After reading your descriptive comments and your suggestion to add in an option to always fork subshells, in my opinion that works to allow ksh users to transition their scripts from using Also based on your descriptive comments and insights, this subshell forking option will go away as command groupings (The New Kornshell, p173) of the subshell grouping type, aka I have been researching into why associative arrays have such memory leaks whereas indexed arrays and compound variables do not show such memory leaks in subshells. {
unset arr
for ((i=1; i<=3; i++)); do
for ((n=1; n<=10000; n++)); do
#( ulimit -t unlimited; typeset -A arr=([a]=1 [b]=2 [c]=3); ) # forked, works
( typeset -A arr=([a]=1 [b]=2 [c]=3); ) # leak
#( typeset -a arr=(a b c); ) # works
#( typeset -C arr=(a=1;b=2;c=3); ) # works
done
ps -p "$$" -o rss=,vsz= # numbers should be stable
done
typeset -p arr
} I found a 2015-02-10 email from David Korn entitled, "Re: [ast-users] [WORKING PATCH] Leak on unset of associative array" with a patch that has been applied to ksh93u+m. It is my thought that ksh93's associative array logic originally did not possess removal/cleanup logic based on a Paulo César Pereira de Andrade's submitted patch. Also, I believe Paulo's patch may not have accounted for subshells as that was not in his provided test case. Since index arrays and compound variables work, I was going to focus on why those work and and possibility adjust those code part(s) in support of associative arrays. Additionally, it may be that Paulo's patch needs adjustment to reference a subshell's namespace/dictionary. Lines 1294 to 1309 in 4491bc6
|
Thanks for reminding me of that, @hyenias. I took a closer look and inserted some debug output calls: --- a/src/cmd/ksh93/sh/name.c
+++ b/src/cmd/ksh93/sh/name.c
@@ -1293,11 +1293,14 @@ void nv_delete(Namval_t* np, Dt_t *root, int flags)
{
if(dtdelete(root,np))
{
+error(ERROR_warn(0),"[DEBUG] one: %s",np->nvname);
if(!(flags&NV_NOFREE) && ((flags&NV_FUNCTION) || !nv_subsaved(np,flags&NV_TABLE)))
{
Namarr_t *ap;
+error(ERROR_warn(0),"[DEBUG] two");
if(nv_isarray(np) && np->nvfun && (ap=nv_arrayptr(np)) && array_assoc(ap))
{
+error(ERROR_warn(0),"[DEBUG] three");
/* free associative array from memory */
while(nv_associative(np,0,NV_ANEXT))
nv_associative(np,0,NV_ADELETE); With this reproducer: for ((i=1; i<=10; i++)); do
for ((n=1; n<=100000; n++)); do
(typeset -A foo=([a]=1 [b]=2 [c]=3))
done
ps -p "$$" -o rss=,vsz=
done …the first ten lines of the output are:
With this reproducer ( for ((i=1; i<=10; i++)); do
for ((n=1; n<=100000; n++)); do
(typeset -A foo=([a]=1 [b]=2 [c]=3); unset foo)
done
ps -p "$$" -o rss=,vsz=
done …the first ten lines of the output are:
With this reproducer (no subshell, does not leak): for ((i=1; i<=10; i++)); do
for ((n=1; n<=100000; n++)); do
typeset -A foo=([a]=1 [b]=2 [c]=3); unset foo
done
ps -p "$$" -o rss=,vsz=
done …the first ten lines of the output are:
So, something in commits 461a1ae and/or e70925c is very broken. |
Commit
They're not dead code, though. If we delete those lines, the regression test introduced in e70925c fails: ksh/src/cmd/ksh93/tests/leaks.sh Lines 155 to 167 in 4491bc6
|
Unfortunately, a forking workaround for for ((i=1; i<=10; i++)); do
for ((n=1; n<=100000; n++)); do
(foo=([a]=1 [b]=2 [c]=3))
done
ps -p "$$" -o rss=,vsz=
done
for ((i=1; i<=10; i++)); do
for ((n=1; n<=100000; n++)); do
(foo=([a]=1 [b]=2 [c]=3); unset foo)
done
ps -p "$$" -o rss=,vsz=
done
|
FWIW the first version of ksh93 to exhibit this memory leak is ksh93s- 2006-09-12, which is the version that adds |
I've found that this change in ksh93s- is what introduced the memory leak for associative arrays in subshells: --- b/src/cmd/ksh93/sh/name.c
+++ a/src/cmd/ksh93/sh/name.c
@@ -291,8 +291,7 @@ void nv_setlist(register struct argnod *arg,register int flags)
sh.prefix = prefix;
if(nv_isarray(np) && (mp=nv_opensub(np)))
np = mp;
- if(nv_isnull(np))
- nv_setvtree(np);
+ nv_setvtree(np);
continue;
}
cp = arg->argval; Reverting that change fixes the leak in 93s-, but the code appears to have changed substantially since then (the diff doesn't apply cleanly to 93u+m). Regardless, it may help provide clues for fixing the memory leak. |
src/cmd/ksh93/sh/subshell.c: nv_restore(): - Before calling _nv_unset for the to-be-restored variable, turn off the NV_NOFREE attribute for the variable if it is not empty and if the variable from which it is restored is empty and does not have the NV_NOFREE attribute. Fixes: ksh93#94
It looks like @atheik found an effective fix. Many thanks! diff --git a/src/cmd/ksh93/sh/subshell.c b/src/cmd/ksh93/sh/subshell.c
index b5965170..308c2b66 100644
--- a/src/cmd/ksh93/sh/subshell.c
+++ b/src/cmd/ksh93/sh/subshell.c
@@ -353,6 +353,8 @@ static void nv_restore(struct subshell *sp)
if(nv_isarray(mp))
nv_putsub(mp,NIL(char*),ARRAY_SCAN);
nofree = mp->nvfun?mp->nvfun->nofree:0;
+ if(mp->nvalue.cp!=Empty && np->nvalue.cp==Empty)
+ nv_offattr(mp,NV_NOFREE);
_nv_unset(mp,NV_RDONLY|NV_CLONE);
if(nv_isarray(np))
{ |
Well, it's effective for the original reproducer, but there is still a leak when the |
src/cmd/ksh93/sh/subshell.c: nv_restore(): - src/cmd/ksh93/bltins/typeset.c: unall(): - src/cmd/ksh93/sh/name.c: nv_newattr(); - Fixes: ksh93#94
It leaks in different ways when diff --git a/src/cmd/ksh93/bltins/typeset.c b/src/cmd/ksh93/bltins/typeset.c
index e1131990..92f7eb77 100644
--- a/src/cmd/ksh93/bltins/typeset.c
+++ b/src/cmd/ksh93/bltins/typeset.c
@@ -1373,7 +1373,21 @@ static int unall(int argc, char **argv, register Dt_t *troot)
nofree_attr = nv_isattr(np,NV_NOFREE); /* note: returns bitmask, not boolean */
if(!nv_isnull(np) || nv_size(np) || nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)))
+ {
+ Namarr_t *ap;
+ if(sh.subshell && nv_isattr(np,NV_ARRAY|NV_NOFREE)==NV_ARRAY && (ap=nv_arrayptr(np)) && array_assoc(ap))
+ {
+ Namval_t *onp = nv_associative(np,0,NV_ACURRENT);
+ while(nv_associative(np,0,NV_ANEXT))
+ {
+ Namval_t *mp = nv_associative(np,0,NV_ACURRENT);
+ if(mp && mp->nvalue.cp && mp->nvalue.cp!=Empty)
+ nv_offattr(mp,NV_NOFREE);
+ }
+ nv_associative(np,(char*)onp,NV_ASETSUB);
+ }
_nv_unset(np,0);
+ }
if(troot==sh.var_tree && sh.st.real_fun && (dp=sh.var_tree->walk) && dp==sh.st.real_fun->sdict)
nv_delete(np,dp,NV_NOFREE);
else if(isfun)
diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c
index b0c085d5..0224bb84 100644
--- a/src/cmd/ksh93/sh/name.c
+++ b/src/cmd/ksh93/sh/name.c
@@ -3056,6 +3056,8 @@ void nv_newattr (register Namval_t *np, unsigned newatts, int size)
free(cp);
cp = 0;
}
+ if(sh.subshell && nv_isattr(np,NV_ARRAY|NV_RDONLY)==NV_ARRAY && sp && sp!=Empty && sp!=Null)
+ free((void*)sp);
}
while(ap && nv_nextsub(np));
#if SHOPT_FIXEDARRAY
diff --git a/src/cmd/ksh93/sh/subshell.c b/src/cmd/ksh93/sh/subshell.c
index e0bab084..e7cd0c41 100644
--- a/src/cmd/ksh93/sh/subshell.c
+++ b/src/cmd/ksh93/sh/subshell.c
@@ -352,6 +352,16 @@ static void nv_restore(struct subshell *sp)
if(nv_isarray(mp))
nv_putsub(mp,NIL(char*),ARRAY_SCAN);
nofree = mp->nvfun?mp->nvfun->nofree:0;
+ if(np->nvalue.cp==Empty)
+ {
+ if(nv_isnull(mp) && !nv_isvtree(np))
+ {
+ free((void*)mp);
+ goto skip;
+ }
+ if(mp->nvalue.cp && mp->nvalue.cp!=Empty)
+ nv_offattr(mp,NV_NOFREE);
+ }
_nv_unset(mp,NV_RDONLY|NV_CLONE);
if(nv_isarray(np))
{
diff --git a/src/cmd/ksh93/tests/leaks.sh b/src/cmd/ksh93/tests/leaks.sh
index 05d8040b..fcb0d5cd 100755
--- a/src/cmd/ksh93/tests/leaks.sh
+++ b/src/cmd/ksh93/tests/leaks.sh
@@ -167,9 +167,15 @@ DO
DONE
# https://github.com/ksh93/ksh/issues/94
-TEST title='defining associative array in subshell' known=y url=https://github.com/ksh93/ksh/issues/94
+TEST title='defining associative array in subshell'
DO
(typeset -A foo=([a]=1 [b]=2 [c]=3))
+ (typeset -Ai foo=([a]=1 [b]=2 [c]=3))
+DONE
+TEST title='defining and unsetting associative array in subshell'
+DO
+ (typeset -A foo=([a]=1 [b]=2 [c]=3); unset foo)
+ (typeset -Ai foo=([a]=1 [b]=2 [c]=3); unset foo)
DONE
# ======
The change to |
Unfortunately, on my system that patch makes two tests (in functions.sh and namespace.sh) crash with attempts to free unallocated pointers. The stack trace is the same in both cases:
|
diff --git a/src/cmd/ksh93/sh/array.c b/src/cmd/ksh93/sh/array.c
index b103ce88b..7c7153d62 100644
--- a/src/cmd/ksh93/sh/array.c
+++ b/src/cmd/ksh93/sh/array.c
@@ -1656,6 +1656,8 @@ void *nv_associative(register Namval_t *np,const char *sp,int mode)
switch(mode)
{
case NV_AINIT:
+ if(sh.subshell && !sh.subshare)
+ sh_subfork(); /* work around https://github.com/ksh93/ksh/issues/94 */
ap = (struct assoc_array*)sh_calloc(1,sizeof(struct assoc_array));
ap->header.table = dtopen(&_Nvdisc,Dtoset);
ap->cur = 0;
diff --git a/src/cmd/ksh93/tests/leaks.sh b/src/cmd/ksh93/tests/leaks.sh
index 05d8040b1..eaa3d3f14 100755
--- a/src/cmd/ksh93/tests/leaks.sh
+++ b/src/cmd/ksh93/tests/leaks.sh
@@ -167,9 +167,14 @@ DO
DONE
# https://github.com/ksh93/ksh/issues/94
-TEST title='defining associative array in subshell' known=y url=https://github.com/ksh93/ksh/issues/94
+TEST title='defining associative array in subshell'
DO
(typeset -A foo=([a]=1 [b]=2 [c]=3))
+ (typeset -Ai foo=([a]=1 [b]=2 [c]=3))
+ (typeset -A foo=([a]=1 [b]=2 [c]=3); unset foo)
+ (typeset -Ai foo=([a]=1 [b]=2 [c]=3); unset foo)
+ (foo=([a]=1 [b]=2 [c]=3))
+ (foo=([a]=1 [b]=2 [c]=3); unset foo)
DONE
# ====== Edit: I spoke too soon. That patch causes regressions:
|
When doing this in a virtual/non-forked subshell:
a memory leak occurs. The memory occupied by the array is not freed when exiting the subshell.
A reproducer with
ps
can confirm this bug exists on 93u+ 2012-08-01:Output on macOS:
Reproducer with
vmstate
,which is compiled into 93u+m by default(edit: as of f9364b1, you have to pass-D_AST_vmalloc
to enable it):Output (note increasing
busy
and decreasingfree
):The text was updated successfully, but these errors were encountered: