1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
Commit graph

441 commits

Author SHA1 Message Date
Johnothan King
5bd18322e0 Remove unnecessary sh.defpathlist variable (re: e3a1dda9) (#461)
src/cmd/ksh93/{sh/path.c,include/shell.h}:
- The sh.defpathlist variable is never set once, which makes every
  use of this variable unnecessary (as it's always null). This
  commit removes sh.defpathlist while also fixing a possible memory
  leak (there was another location where defpathinit() was invoked
  without saving the returned pointer, causing a memory leak).
2022-02-17 19:40:53 +00:00
Martijn Dekker
56c0c24b55 Do not disable completion along with pathname expansion
The -f/--noglob shell option is documented simply as: "Disables
pathname expansion." But after 'set -f' on an interactive shell,
command completion and file name completion also stop working. This
is because they internally use the pathname expansion mechanism.
But it is not documented anywhere that 'set -f' disables
completion; it's just a side effect of an implementation detail.

Though ksh has always acted like this, I think it should change
because it's not useful or expected behaviour. Other shells like
bash, yash or zsh don't act like this.

src/cmd/ksh93/sh/expand.c,
src/cmd/ksh93/sh/macro.c:
- Allow the SH_COMPLETE (command completion) or SH_FCOMPLETE
  (file name completion) state bit to override SH_NOGLOB in
  path_generate() and in sh_macexpand().
2022-02-17 19:38:15 +00:00
Martijn Dekker
1cdd963f53 do not save file desc state for subshares (re: 6304dfce, fb755163)
The >&- redirection subshell leak fixed in 6304dfce still existed
for shared-state ${ command substitutions; } a.k.a. subshares,
which cannot be forked.

I previously noticed that sh_subsavefd() saves the FD state even
for subshares. That seems logically incorrect as subshares share
their state with the invoking environment by definition.

src/cmd/ksh93/sh/subshell.c: sh_subsavefd():
- Sure enough, adding a check for !sh.subshare fixes the bug.
- Use the sh.subshell counter and not the subshell data pointer to
  check for a virtual subshell. If the shell is reinitialised in a
  fork to execute a new script (see 0af81992), any parent virtual
  subshell data is currently not cleared as it is locally scoped to
  subshell.c, so that check would be incorrect then.

src/cmd/ksh93/sh/io.c: sh_redirect:
- Remove now-redundant (and actually incorrectly placed) check for
  sh.subshare added in fb755163.
2022-02-17 19:36:50 +00:00
Martijn Dekker
6304dfce41 Fix corner-case >&- redirection leak out of subshell
Reproducer:

    exec 9>&1
    ( { exec 9>&1; } 9>&- )
    echo "test" >&9 # => 9: cannot open [Bad file descriptor]

The 9>&- incorrectly persists beyond the { } block that it
was attached to *and* beyond the ( ) subshell. This is yet another
bug with non-forking subshells; forking it with something like
'ulimit -t unlimited' works around the bug.

In over a year we have not been able to find a real fix, but I came
up with a workaround that forks a virtual subshell whenever it
executes a code block with a >&- or <&- redirection attached. That
use case is obscure enough that it should not cause any performance
regression except in very rare corner cases.

src/cmd/ksh93/sh/xec.c: sh_exec(): TSETIO:
- This is where redirections attached to code blocks are handled.
  Check for a >&- or <&- redirection using bit flaggery from
  shnodes.h and fork if we're executing such in a virtual subshell.

Resolves: https://github.com/ksh93/ksh/issues/161
Thanks to @ko1nksm for the bug report.
2022-02-17 19:35:47 +00:00
Martijn Dekker
14a43a0a88 Yet more misc. cleanups; rm SHOPT_PFSH, SHOPT_TYPEDEF
Notable changes:
- Remove SHOPT_PFSH compile-time option and associated code.
  This was meant to work with Solaris rights profiles, see:
  https://docs.oracle.com/cd/E23824_01/html/821-1461/profiles-1.html#REFMAN1profiles-1
  But it has been obsolete for years as Solaris stopped using
  it in its shipped ksh several OS versions ago, preferring a
  library-based wrapper around ksh and other shells.
  Nonetheless I experimented with the option on Solaris 11.4.
  Result: no external command will run; output of unitialised
  memory in error message. So it's already fallen victim to bit
  rot. There's nothing interesting here, so just get rid.
- Remove SHOPT_TYPEDEF compile-time option (but keep the code!).
  Turning it off caused the build to fail. It may be possible to
  fix it, but the type definition code is integral to ksh now (e.g.
  'enum' depends on much of it) so it makes no sense to disable it.
  This was removed in the ksh 93v- beta version as well.
- Remove nv_close() calls and remove nv_close() documentation from
  the nval.3 man page. This function is a dummy, present without
  any changes since the beginning of the ast-open-archive repo in
  1995. The comment was: "Currently this is a dummy, but someday
  will be needed for reference counting". 27 or more years later,
  it's time to admit it's never going to happen. (And of course,
  nv_close() calls were not being used with anything resembling
  consistency.)
- Add a null nv_close() macro to nval.h for compatibility with
  third party code that follows the old documentation.
- Add a few missing regression tests.
2022-02-10 21:04:56 +00:00
Johnothan King
a00fe6b7fd Fix one buffer overflow in 'typeset -p .sh.type' (#457)
This small commit replaces one instance of memcmp with strncmp to
fix one of the buffer overflows that causes 'typeset -p .sh.type'
to crash (see also https://github.com/ksh93/ksh/issues/456).
2022-02-10 21:04:45 +00:00
Johnothan King
f38494ea1d Fix multiple bugs in .sh.match (#455)
This commit backports all of the relevant .sh.match bugfixes from
ksh93v-. Most of the .sh.match rewrite is from versions 2012-08-24
and 2012-10-04, with patches from later releases of 93v- and
ksh2020 also applied. Note that there are still some remaining bugs
in .sh.match, although now the total count of .sh.match bugs should
be less that before.

These are the relevant changes in the ksh93v- changelog that were
backported:
12-08-07  .sh.match no longer gets set for patterns in PS4 during
          set -x.
12-08-10  Rewrote .sh.match expansions fixing several bugs and
          improving performance.
12-08-22  .sh.match now handles subpatterns that had no matches with
          ${var//pattern} correctly.
12-08-21  A bug in setting .sh.match after ${var//pattern/string}
          when string is empty has been fixed.
12-08-21  A bug in setting .sh.match after [[ string == pattern ]]
          has been fixed.
12-08-31  A bug that could cause a core dump after
          typeset -m var=.sh.match has been fixed.
12-09-10  Fixed a bug in typeset -m the .sh.match is being renamed.
12-09-07  Fixed a bug in .sh.match code that coud cause the shell
          to quitely
13-02-21  The 12-01-16 bug fix prevented .sh.match from being used
          in the replacement string. The previous code was restored
          and a different fix which prevented .sh.match from being
          computed for nested replacement has been used instead.
13-05-28  Fixed two bug for typeset -c and typeset -m for variable
          .sh.match.

Changes:
- The SHOPT_2DMATCH option has been removed. This was already the
  default behavior previously, and now it's documented in the man
  page.
- init.c: Backported the sh_setmatch() rewrite from 93v- 2012-08-24
  and 2012-10-04.
- Backported the libast 93v- strngrpmatch() function, as the
  .sh.match rewrite requires this API.
- Backported the sh_match regression tests from ksh93v-, with many
  other sh_match tests backported from ksh2020. Much of the sh_match
  script is based on code from Roland Mainz:
  https://marc.info/?l=ast-developers&m=134606574109162&w=2
  https://marc.info/?l=ast-developers&m=134490505607093
- tests/{substring,treemove}.sh: Backported other relevant .sh.match
  fixes, with tests added to the substring and treemove test scripts.
- tests/types.sh: One of the (now reverted) memory leak bugfixes
  introduced a CI test failure in this script, so for that test the
  error message has been improved.
- string/strmatch.c: The original ksh93v- code for the strngrpmatch()
  changes introduced a crash that could occur because strlen would
  be used on a null pointer. This has been fixed by avoiding strlen
  if the string is null.

One nice side effect of these changes is a considerable performance
improvement in the shbench[1] gsub benchmark (results from 20
iterations with CCFLAGS=-Os):
--------------------------------------------------
name      /tmp/ksh-current     /tmp/ksh-matchfixes
--------------------------------------------------
gsub.ksh  0.883 [0.822-0.959]  0.457 [0.442-0.505]
--------------------------------------------------

Despite all of the many fixes and improvements in the backported
93v- .sh.match code, there are a few remaining bugs:

- .sh.match is printed with a default [0] subscript (see also
  https://github.com/ksh93/ksh/issues/308#issuecomment-1025016088):
     $ arch/*/bin/ksh -c 'echo ${!.sh.match}'
       .sh.match[0]
  This bug appears to have been introduced by the changes from
  ksh93v- 2012-08-24.
- The wrong variable name is given for 'parameter not set' errors
  (from https://marc.info/?l=ast-developers&m=134489094602596):
     $ arch/*/bin/ksh -u
     $ x=1234
     $ true "${x//~(X)([012])|([345])/}"
     $ compound co
     $ typeset -m co.array=.sh.match
     $ printf "%q\n" "${co.array[2][0]}"
     arch/linux.i386-64/bin/ksh: co.array[2][(null)]: parameter not set
- .sh.match leaks out of subshells. Further information and a
  reproducer can be found here:
  https://marc.info/?l=ast-developers&m=136292897330187

[1]: https://github.com/ksh-community/shbench
2022-02-10 21:04:23 +00:00
Martijn Dekker
232b7bff30 Fix multiple bugs in executing scripts without a #! path
When executing a script without a hashbang path like #!/bin/ksh,
ksh forks itself, longjmps back to sh_main(), and then (among other
things) calling sh_reinit() which is the function that tries to
reinitialise as much of the shell as it can. This is its way of
ensuring the child script is run in ksh and not some other shell.

However, this appraoch is incredibly buggy. Among other things,
changes in built-in commands and custom type definitions survived
the reinitialisation, "exporting" variables didn't work properly,
and the hash table and ${.sh.stats} weren't reset. As a result,
depending on what the invoking script did, the invoked script could
easily fail or malfunction.

It is not actually possible to reinitialise the shell correctly,
because some of the shell state is in locally scoped static
variables that cannot simply be reinitialised. There are probably
huge memory leaks with this approach as well. At some point, all
this is going to need a total redesign. Clearly, the only reliable
way involves execve(2) and a start from scratch.

For now though, this seems to fix the known bugs at least. I'm sure
there are more to be discovered.

This commit makes another change: instead of the -h/trackall option
(which has been a no-op for decades), the posix option is now
inherited by the child script. Since there is no hashbang path from
which to decide whether the shell should run in POSIX mode nor not,
the best guess is probably the invoking script's setting.

src/cmd/ksh93/sh/init.c: sh_reinit():
- Keep the SH_INIT state on during the entire procedure.
- Delete remaining non-exported, non-default variables.
- Remove attributes from exported variables. In POSIX mode, remove
  all attributes; otherwise, only remove readonly.
- Unset discipline function pointers for variables.
- Delete all custom types.
- Delete all functions and built-ins, then reinitialise the built-ins
  table from scatch.
- Free the alias values before clearing the alias table.
- Same with hash table entries (tracked aliases).
- Reset statistics.
- Inherit SH_POSIX instead of SH_TRACKALL.
- Call user init function last, not somewhere in the middle.

src/cmd/ksh93/sh/name.c: sh_envnolocal():
- Be sure to preserve the export attribute of kept variables.

Resolves: https://github.com/ksh93/ksh/issues/350
2022-02-10 21:03:43 +00:00
Johnothan King
7d4c7d9156 Fix 'typeset -p' output of compound array types (#453)
This bugfix was backported from ksh93v- 2012-10-04. The bug fixed
by this change is one that causes 'typeset -p' to omit the -C flag
when listing compound arrays belonging to a type:

   $ typeset -T Foo_t=(compound -a bar)
   $ Foo_t baz
   $ typeset -p baz.bar
   typeset -a baz.bar=''  # This should be 'typeset -C -a'

src/cmd/ksh93/sh/nvtype.c:
- Backport change from 93v- 2012-10-04 that sets the array nvalue to
  a pointer named Null (which is "") in nv_mktype(), then to Empty
  in fixnode().
- Change the Null name from the 93v- code to AltEmpty to avoid
  misleading code readers into thinking that it's a null pointer.

src/cmd/ksh93/tests/types.sh:
- Backport the relevant 93v- changes to the types regression tests.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-02-10 21:03:24 +00:00
Johnothan King
787058bdbf Fix the output of typeset -p for two dimensional indexed arrays (#454)
In ksh93v- 2012-10-04 the following bugfix is noted in the changelog
(this fix was most likely part of ksh93v- 2012-09-27, although that
version is not archived anywhere):
12-09-21  A bug in which the output of a two dimensional sparse
          indexed array would cause the second subscript be treated
          as an associative array when read back in has been fixed.
          Elements that are sparse indexed arrays now are prefixed
          type "typeset -a".

Below is a before and after of this change:

   # Before
   $ typeset -a foo[1][2]=bar
   $ typeset -p foo
   typeset -a foo=([1]=([2]=bar) )

   # After
   $ typeset -a foo[1][2]=bar
   $ typeset -p foo
   typeset -a foo=(typeset -a [1]=([2]=bar) )

src/cmd/ksh93/sh/*.c:
- Backport changes from ksh93v- to print 'typeset -a' before sparse
  indexed arrays and properly handle 'typeset -a' in reinput
  commands from 'typeset -p'.

src/cmd/ksh93/tests:
- Add two regression tests to arrays.sh for this change.
- Update the existing regression tests for compatibility with the
  new printed typeset output.
2022-02-10 21:01:40 +00:00
Martijn Dekker
e6d0187dd8 Don't allow 'enum' and 'typeset -T' to override special built-ins
Special builtins are undeleteable for a reason. But 'enum' and
'typeset -T' allow overriding them, causing an inconsistent state.

@JohnoKing writes:
| The behavior is rather buggy, as it appears to successfully
| override normal builtins but fails to delete the special
| builtins, leading to scenarios where both the original builtin
| and type are run:
|
| $ typeset -T eval=(typeset BAD; typeset TYPE)  # This should have failed
| $ eval foo=BAD
| /usr/bin/ksh: eval: line 1: foo: not found
| $ enum trap=(BAD TYPE)   # This also should have failed
| $ trap foo=BAD
| /usr/bin/ksh: trap: condition(s) required
| $ enum umask=(BAD TYPE)
| $ umask foo=BAD
| $ echo $foo
| BAD
|
| # Examples of general bugginess
| $ trap bar=TYPE
| /usr/bin/ksh: trap: condition(s) required
| $ echo $bar
| TYPE
| $ eval var=TYPE
| /usr/bin/ksh: eval: line 1: var: not found
| $ echo $var
| TYPE

This commit fixes the following:

The 'enum' and 'typeset -T' commands are no longer allowed to
override and replace special built-in commands, except for type
definition commands previously created by these commands; these
are already (dis)allowed elsewhere.

A command like 'typeset -T foo_t' without any assignments no longer
creates an incompletely defined 'foo_t' built-in comamnd. Instead,
it is now silently ignored for backwards compatibility. This did
have a regression test checking for it, but I'm changing it because
that's just not a valid use case. An incomplete type definition
command does nothing useful and only crashes the shell when run.

src/cmd/ksh93/bltins/enum.c: b_enum():
- Do not allow overriding non-type special built-ins.

src/cmd/ksh93/sh/name.c: nv_setlist():
- Do not allow 'typeset -T' to override non-type special built-ins.
  To avoid an inconsistent state, this must be checked for while
  processing the assignments list before typeset is really invoked.

src/cmd/ksh93/bltins_typeset.c: b_typeset():
- Only create a type command if sh.envlist is set, i.e., if some
  shell assignment(s) were passed to the 'typeset -T' command.

Progresses: https://github.com/ksh93/ksh/issues/350
2022-02-10 21:01:00 +00:00
Martijn Dekker
65aff0befb Fix conditional expansions ${array[i]=value}, ${array[i]?error}
$ unset foo
    $ echo ${foo[42]=bar}
    (empty line)

Instead of the empty line, 'bar' was expected. As foo[42] was
unset, the conditional assignment should have worked.

    $ unset foo
    $ : ${foo[42]?error: unset}
    (no output)

The expansion should have thrown an error with the given message.

This bug was introduced in ksh 93t 2008-10-01. Thanks to @JohnoKing
for finding the breaking change.

Analysis: The problem was experimenally determined to be in in the
following lines of nv_putsub(). If the array member is unset (i.e.
null), the value is set to the empty string instead:

src/cmd/ksh93/sh/array.c
1250: 		else
1251:			ap->val[size].cp = Empty;

It makes some sense: if there is a value (even an empty one), the
variable is set and these expansions should behave accordingly.
Sure enough, deleting these lines fixes the bug, but at the expense
of introducing a lot of other array-related regressions. So we need
a way to special-case the affected expansions.

Where to do this? If we replace line 1251 with an abort(3) call, we
get this stack trace:

0   libsystem_kernel.dylib    __pthread_kill + 10
1   libsystem_pthread.dylib   pthread_kill + 284
2   libsystem_c.dylib         abort + 127
3   ksh                       nv_putsub + 1411 (array.c:1255)
4   ksh                       nv_endsubscript + 940 (array.c:1547)
5   ksh                       nv_create + 4732 (name.c:1066)
6   ksh                       nv_open + 1951 (name.c:1425)
7   ksh                       varsub + 4934 (macro.c:1322)
[rest omitted]

The special-casing needs to be done on line 1250 of array.c, but
flagged in varsub() which processes these expansions. So, varsub()
calls nv_open() calls nv_create() calls nv_endsubscript() calls
nv_putsub(). That's a fairly deep call stack, so passing an extra
flag argument does not seem doable. I did try an approach using a
couple of new bit flags passed via these functions' flags and mode
parameters, but the way this code base uses bit flags is so
intricate, it does not seem to be possible to add or change
anything without unwanted side effects in all sorts of places.

So the only fix I can think of adds yet another global flag
variable for a very special case. It's ugly, but it works.
An elegant fix would probably involve a fairly comprehensive
redesign, which is simply not going to happen.

src/cmd/ksh93/include/shell.h:
- Add global sh.cond_expan flag.

src/cmd/ksh93/sh/array.c: nv_putsub():
- Do not set value to empty string if sh.cond_expan is set.

src/cmd/ksh93/sh/macro.c: varsub():
- Set sh.cond_expan flag while calling nv_open() for one of the
  affected expansions.
- Minor refactoring for legibility and to make the fix fit better.
- SSOT: Instead of repeating string "REPLY", use the node's nvname.
- Do not pointlessly add an extra 0 byte when saving id for error
  message; sfstruse() already adds this.

Thanks to @oguz-ismail for the bug report.

Resolves: https://github.com/ksh93/ksh/issues/383
2022-02-05 23:39:16 +00:00
Martijn Dekker
493a31053e Do not export variables with dot names (re: 8e72608c)
Variables with a dot in their name, such as those declared in
namespace { ... } blocks, are usually stored in a separate tree
with their actual names not containing any dots. But under some
circumstances, including at least direct assignment of a
non-preexisting dot variable, dot variables are stored in the main
sh.var_tree with names actually containing dots. With allexport
active, those could end up exported to the environment. This bug
was also present in previous release versions of ksh.

src/cmd/ksh93/sh/name.c: pushnam():
- Check for a dot in the name before pushing a variable to export.
2022-02-05 15:08:50 +00:00
Johnothan King
a8dd1bbd9d typeset -p: fix output of nonexistent [0]= array element (#451)
This fix was backported from ksh 93v- 2012-10-04.

src/cmd/ksh93/sh/nvtree.c: nv_outnode():
- If the array is supposed to be empty, do not continue. This
  avoids outputting a nonexistent [0]= element for empty arrays.

Resolves: https://github.com/ksh93/ksh/issues/420
Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-02-05 13:53:51 +00:00
Johnothan King
fb696ecfae trap: fix use after free (#446)
This commit adds a fix for the trap command, backported from a fork
of ksh2020: 2033375f

src/cmd/ksh93/sh/jobs.c: job_chldtrap():
- Fixed a use after free bug in the for loop. The string pointed to
  by sh.st.trapcom[SIGCHLD] may be freed from memory after
  sh_trap(), so it must be reobtained each time sh_trap() is called
  from within the for loop.
2022-02-05 13:53:11 +00:00
Johnothan King
8e72608c1c Export all variables assigned to while allexport is on (#431)
All variables that are assigned a value should be exported while
the allexport shell option is enabled. This works in most cases,
but variables assigned to with ${var:=foo} or $((var=123)) aren't
exported while allexport is on.

src/cmd/ksh93/sh/name.c:
- nv_putval(): This is the central assignment function; all forms
  of variable assignment end up here. So this is the best place
  to check for SH_ALLEXPORT and turn on the export attribute.
- nv_setlist(): Remove allexport handling, now redundant.

src/cmd/ksh93/bltins/read.c: sh_readline():
- Remove allexport handling, now redundant.

src/cmd/ksh93/sh/main.c: sh_main():
- nv_putval() is used to initialize PS4 and IFS using nv_putval();
  this is after an -a/--allexport specified on the ksh command
  line has been processed, so temporarily turn that off.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-02-05 13:52:28 +00:00
Johnothan King
0863a8eb29 Support glibc 2.35's posix_spawn_file_actions_addtcsetpgrp_np(3)
This commit implements support for the glibc 2.35
posix_spawn_file_actions_addtcsetpgrp_np(3) extension[2][3],
updating spawnveg(3) to use the new function for setting the
terminal group. This was done with the intention of improving
performance in interactive shells without reintroducing previous
race conditions[4][5].

[1]: https://sourceware.org/pipermail/libc-alpha/2022-February/136040.html
[2]: https://sourceware.org/git/?p=glibc.git;a=commit;h=342cc934
[3]: https://sourceware.org/git/?p=glibc.git;a=commit;h=6289d28d
[4]: https://github.com/ksh93/ksh/issues/79
[5]: https://www.mail-archive.com/ast-developers@research.att.com/msg00717.html

src/cmd/ksh93/sh/path.c:
- Tell spawnveg(3) to set the terminal process group when launching
  a child process in an interactive shell.

src/cmd/ksh93/sh/xec.c:
- If posix_spawn_file_actions_addtcsetpgrp_np(3) is available,
  allow use of spawnveg(3) (via sh_ntfork()) even with job control
  active.
- sh_ntfork(): Reimplement most of the SIGTSTP handling code
  removed in commit 66c37202.

src/lib/libast/comp/spawnveg.c,
src/lib/libast/misc/procopen.c,
src/lib/libast/features/sys:
- Add support for posix_spawn_file_actions_addtcsetpgrp_np(3).
- Allow spawnveg to set the terminal process group when pgid == 0.
  This was necessary to avoid race conditions when using the new
  function.

src/lib/libast/features/lib:
- Detect posix_spawn_file_actions_addtcsetpgrp_np(3).
- Do not detect an OS spawnveg(3). With the API changes to spawnveg
  in this pull request ksh probably can't use the OS's spawnveg
  function anymore. (That's assuming anything else even provides a
  spawnveg function to begin with, which is unlikely.)

src/lib/libast/features/api,
src/cmd/ksh93/include/defs.h:
- Bump libast version (20220101 => 20220201) due to the spawnveg(3)
  API change.

src/lib/libast/man/spawnveg.3:
- Document the changes to spawnveg(3) in the corresponding man
  page. Currently, it will only use the new tcfd argument if
  posix_spawn_file_actions_addtcsetpgrp_np(3) is supported. This
  could also be implemented for the fork(2) fallback, but for now
  I've avoided changing that since actually using it in the fork
  code would likely require a lot of hackery to avoid attempting
  tcsetpgrp with vfork (the behavior of tcsetpgrp after vfork is
  not portable) and would only benefit systems that don't have
  posix_spawn and vfork (I can't recall any off the top of my head
  that would fall under that category).
- Updated the man page to account for spawnveg's change in
  behavior.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-02-05 13:31:31 +00:00
Martijn Dekker
1375cda934 Default to emacs upon invoking interactive shell
If the VISUAL or EDITOR environment variable is not set to a value
matching *[Vv][Ii]* or *macs* at initialisation time, then ksh does
not turn on any line editor.

This is user-hostile. New users on Unix-like systems typically have
a simple editor like nano preconfigured as their default, or may
not have the VISUAL or EDITOR variable set at all. So if they try
ksh, they find themselves without basic functionality such as arrow
keys and probably go straight back to bash.

The emacs line editor is by far the most widely used, especially
among new users, so ksh should default to that. Most other shells
already do this.

src/cmd/ksh93/sh/main.c: sh_main():
- On an interactive shell, if on editor was turned on based on
  $VISUAL or $EDITOR, turn on emacs before reading input.
2022-02-01 23:47:56 +00:00
Martijn Dekker
d650c73e55 Fix SIGINT handling for external commands run from scripts
Reproducer:

  $ ksh -c 'bash -c '\''kill -s INT $$'\''; echo "$?, continuing"'

Expected result: output "258, continuing"; exit status 0.

Actual result: no output; exit status 258. The child process sent
SIGINT only to itself and not to the process group, so the parent
script was wrongly interrupted.

Every shell except ksh93 produces the expected result. ksh93 also
gave the expected result before version 2008-01-31 93s+, which
introduced the code below.

Analysis: The problem is in these lines of code in xec.c,
sh_exec(), TFORK case, parent branch of fork:

1649:	if(!sh_isstate(SH_MONITOR))
1650:	{
1651:		if(!(sh.sigflag[SIGINT]&(SH_SIGFAULT|SH_SIGOFF)))
1652:			sh_sigtrap(SIGINT);
1653:		sh.trapnote |= SH_SIGIGNORE;
1654:	}
[...pipe and I/O handling, wait for command to finish...]
1667:	if(!sh_isstate(SH_MONITOR))
1668:	{
1669:		sh.trapnote &= ~SH_SIGIGNORE;
1700:		if(sh.exitval == (SH_EXITSIG|SIGINT))
1701:			kill(sh.current_pid,SIGINT);
1702:	}

When a user presses Ctrl+C, SIGINT is sent to the entire process
group. If job control is fully off (i.e., !sh_isstate(SH_MONITOR)),
then the process group includes the parent script. Therefore, in a
script such as

  $ ksh -c 'bash -c '\''read x'\''; echo "$?, continuing"'

when the user presses Ctrl+C while bash waits for 'read x' input,
the parent ksh script should be interrupted as well.

Now, the code above ignores SIGINT while bash is running. (This is
done using special-casing in sh_fault() to handle that SH_SIGIGNORE
flag for SIGINT.) So, when Ctrl+C interrupts the process group, the
parent script is not getting interrupted as it should.

To compensate for that, the code then detects, using sh.exitval
(the child process' exit status), whether the child process was
killed by SIGINT. If so, it simply assumes that the signal was
meant for the process group including the parent script, so it
reissues SIGINT to itself after unignoring it.

But, as we can see from the broken reproducer above, that
assumption is not valid. Scripts are perfectly free to send SIGINT
to themselves only, and that must work as expected.

src/cmd/ksh93/sh/xec.c: sh_exec(): TFORK: parent branch:
- Instead of ignoring SIGINT, sigblock() it, which delays handling
  the signal until sigrelease(). (Note that these are macros
  defined in src/cmd/ksh93/features/sigfeatures according to OS
  capabilities.)
- This makes reissuing SIGINT redundant, so delete that, which
  fixes the bug.

src/cmd/ksh93/sh/fault.c:
- Nothing now sets the SH_SIGIGNORE flag in sh.trapnote, so remove
  special-casing added in 2008-01-31 93s+.
2022-02-01 05:50:10 +00:00
Martijn Dekker
9c313c7fe3 Update copyright years in files changed since 1st Jan 2022 2022-01-30 20:49:04 +00:00
Johnothan King
ad9f9ff13e Accumulated fixes for minor issues (#442)
This commit applies various accumulated bugfixes:

- Applied some fixes for compiler warnings based off of the
  following pull requests (with whitespace changes excluded to
  avoid inflating the size of the diff):

  https://github.com/att/ast/pull/281
  https://github.com/att/ast/pull/283
  https://github.com/att/ast/pull/303
  https://github.com/att/ast/pull/304

- clone_type(): Two separate variables in this function share the
  same name. A bugfix from ksh93v- 2013-05-24 was backported to
  avoid conflict issues.

- Backported a minor error message improvement from ksh2020 for
  when the user attempts to run too many coprocesses.

- Backported a ksh2020 bugfix for a file descriptor leak:
  58bc8b56

- Backported ksh2020 bugfixes for unused variable and pointless
  assignment lint warnings:
  47650fe0
  df209c0d
  5e417b00

- Applied a few minor improvements to libast from graphviz:
  e7c03541
  969a7cde

- dtmemory(): Mark unused parameters with NOT_USED(). Based on:
  6ac3ad99

- Applied a few documentation/comment tweaks for the NEWS file,
  printf -v and spawnveg.

- Added a missing regression test for using the rm builtin's -f
  option without additional arguments (this was fixed in
  ksh93u+ 2012-02-14).
2022-01-30 20:42:59 +00:00
Johnothan King
e3a1dda93a Fix memory leak in defpathinit() (#441)
Currently, running ksh under ASan without the ASAN_OPTIONS variable
set to 'detect_leaks=0' usually ends with ASan complaining about a
memory leak in defpathinit() (this leak doesn't grow in a loop, so
no regression test was added to leaks.sh). Reproducer:

 $ ENV=/dev/null arch/*/bin/ksh
 $ cp -?
 cp: invalid option -- '?'
 Try 'cp --help' for more information.
 $ exit

 =================================================================
 ==225132==ERROR: LeakSanitizer: detected memory leaks

 Direct leak of 85 byte(s) in 1 object(s) allocated from:
     #0 0x7f6dab42d459 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:154
     #1 0x5647b77fe144 in sh_calloc /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/init.c:265
     #2 0x5647b788fea9 in path_addcomp /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:1567
     #3 0x5647b78911ed in path_addpath /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:1705
     #4 0x5647b7888e82 in defpathinit /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:442
     #5 0x5647b78869f3 in ondefpath /home/johno/GitRepos/KornShell/ksh/src/cmd/ksh93/sh/path.c:67
 --- cut ---
 SUMMARY: AddressSanitizer: 174 byte(s) leaked in 2 allocation(s).

Analysis: The previous code was leaking memory because
defpathinit() returns a pointer from path_addpath(), which has
memory allocated to it in path_addcomp(). This is the code ASan
traced as having allocated memory:

442:	return(path_addpath((Pathcomp_t*)0,(defpath),PATH_PATH));

In path_addpath():
1705:	first = path_addcomp(first,old,cp,type);
[...]
1729:	return(first);

In path_addcomp():
1567:	pp = sh_newof((Pathcomp_t*)0,Pathcomp_t,1,len+1);

The ondefpath() function doesn't save a reference to the pointer
returned by defpathinit(), which causes the memory leak:

66:	if(!defpath)
67:		defpathinit();

The changes in this commit avoid this problem by setting the
defpath variable without also calling path_addpath().

src/cmd/ksh93/sh/path.c:
- Move the code for allocating defpath from defpathinit() into its
  own dedicated function called std_path(). This function is called
  by defpathinit() and ondefpath() to obtain the current string
  stored in the defpath variable. This bugfix is adapted from a
  fork of ksh2020: db5c83a6
2022-01-30 20:42:15 +00:00
Martijn Dekker
9e525c5bde array_grow(): fix wrong sizeof()
The array_grow() function calculates the size by multiplying with
sizeof(union Value*), where sizeof(union Value) was clearly meant.
In practice, these are the same size on most (or maybe even all)
systems, as no current member of union Value is larger than a
pointer -- see name.h. But it's still wrong.
2022-01-30 20:41:29 +00:00
Martijn Dekker
304648d0c5 Another round of accumulated tweaks and cleanups
Notable changes:

src/cmd/ksh93/*.c:
- Get rid of all the dtuserdata(FOO,&sh,1) calls backported in
  cc492752. These set pointers to sh in Cdt objects. As of
  b590a9f1, the code does not use any pointers to sh, so these are
  superfluous.

src/cmd/ksh93/data/builtins.c,
src/cmd/ksh93/sh.1:
- As of ksh 93l 2001-06-01, the -h/trackall option has no effect at
  all, so trim its documentation.

src/lib/libast/man/stk.3,
src/lib/libast/man/stak.3:
- Correct the documentation on what the ST(A)K_SMALL option bit
  actually does based on a reading of the code.
- Document the STK_NULL option bit.

README.md,
src/cmd/ksh93/README:
- Add a note that -fdiagnostics-color=always will break the build.
  Ref.: https://github.com/ksh93/ksh/issues/379

src/lib/libast/Mamfile:
- Remove a 'rm -f astmath' command -- a file that is never created.
  But on Cygwin this removes astmath.exe, which *is* used. As a
  result, executing it failed on Cygwin, so the system incorrectly
  detected that Cygwin needs -lm for math functions.
2022-01-28 21:12:31 +00:00
Martijn Dekker
2c35a53964 Fix resuming external command run from POSIX function or dot script
This fixes yet another whopper of a bug in job control. And it's
been in every version of ksh93 back to 1995, the beginning of
ast-open-archive. <sigh>

This is also bug number 23 that is fixed by simply removing code.

Reproducer:

1. Run vi, or another suspendable program, from a dot script or
   POSIX function (ksh handles them the same way). So either:
   $ echo vi >v
   $ . ./v
   or:
   $ v() { vi; }
   $ v

2. Suspend vi by typing Ctrl+Z.

3. Not one but two jobs are registered:
   $ jobs -l
   [2] + 85513	Stopped                  . ./v
   [1] - 85512	Stopped                  . ./v

4. 'fg' does not work on either of them, just printing the job
   command name but not resuming the editor. The second job
   disappears from the table after 'fg'.
   $ fg %2
   . ./v
   $ fg %2
   ksh: fg: no such job
   $ fg %1
   . ./v
   $ fg %1
   . ./v

Either way, you're stuck with an unresumable vi that you have to
'kill -9' manually.

src/cmd/ksh93/sh/xec.c: sh_exec(): TFORK:
- Do *not* turn off the SH_MONITOR state flag (which tells ksh to
  keep track of jobs) when running an external command from a
  profile script or dot script/POSIX function. It's obvious that
  this results in an inconsistent job control state as ksh will not
  update it when the external command is suspended. The purpose of
  this nonsense is surely lost to history as it's been there since
  1995 or before. My testing says that removing it doesn't break
  anything. If that turns out to be wrong, then the breakage will
  have to be fixed in a correct way instead.
2022-01-28 05:15:42 +00:00
Johnothan King
c0567c5e1d Fix spurious syntax error when using ${foo[${bar}..${baz}]} (#436)
Attempting to use array subscript expansion with variables that
aren't set currently causes a spurious syntax error (in ksh93u+ and
older commits the reproducer crashes):
   $ ksh -c 'echo ${foo[${bar}..${baz}]}'  # Shouldn't print anything
   ksh: : arithmetic syntax error

src/cmd/ksh93/sh/macro.c:
- Backport a parser bugfix from ksh93v- 2012-08-24 that avoids
  setting mp->dotdot until the copyto() function's loop is
  finished.

src/cmd/ksh93/tests/arrays.sh:
- Add regression tests for this bug.
2022-01-27 16:08:54 +00:00
Martijn Dekker
fe268fcc91 Cygwin: workaround for ksh to execute #!-less scripts with itself
On Cygwin, ksh does not execute scripts without a #! path in a fork
of the ksh process as it does on other systems. Reproducer (run
from ksh):

  $ cat test.sh
  echo "${BASH_VERSION:-not bash}"
  echo "${.sh.version}"
  $ chmod +x test.sh
  $ ./test.sh
  4.4.12(3)-release
  ./test.sh: line 2: ${.sh.version}: bad substitution

The script was executed in bash instead of ksh.

After this fix, the output on Cygwin is like ksh on other systems:

  not bash
  Version AJM 93u+m/1.1.0-alpha+dev 2022-01-26

This also fixes a number of regression test failures, as quite a
few tests create and execute temp scripts without a hashbang path.

Analysis: On Cygwin, execve(2) happily executes shell scripts
without a #! path with /bin/sh (which is bash --posix). However,
ksh relies on execve(2) executing binaries or #! only, as it uses
an ENOEXEC failure to decide whether to fork and execute a #!-less
shell script with a reinitialized copy of itself via exscript().

src/cmd/ksh93/sh/path.c: path_spawn():
- Look at the magic first two bytes of the file; if it is "MZ"
  (Mark Zbikowski, originator of the .exe format) or "#!", continue
  as normal, otherwise simulate an ENOEXEC failure from execve(2)
  which will cause ksh to fall back on #!-less script execution.
2022-01-27 16:08:44 +00:00
Martijn Dekker
172becffea Some more accumulated minor tweaks and cleanups
Notable changes:

src/cmd/ksh93/include/fault.h:
- Get rid of the superflous sh pointer argument in the
  sh_pushcontext() and sh_popcontext() macros. (re: 2d3ec8b6)

src/cmd/ksh93/tests/io.sh:
- Tweak a process substitution test to allow up to a second for
  unused process substitution processes to disappear. On the Alpine
  Linux console (at least the musl libc version), this is needed to
  avoid a test failure as long as no GUI is active. As soon as you
  start X11, this phenomenon disappears, even on the console. Very
  strange, but also very probably not ksh's fault.

src/cmd/ksh93/tests/shtests:
- Instead of just SIGCONT and SIGPIPE, set all signals to default,
  just to be sure to avoid spurious test failures due to signals
  that were ignored on entry. (It made no difference to the
  aforementioned Alpine Linux test failure, so ignored signals had
  nothing to do with that -- but still a good idea.)

.github/workflows/ci.yml:
- On the GitHub CI runs, when testing with SHOPTs disabled, disable
  SHOPT_SPAWN as well, which tests that everything still works
  correctly with the regular fork(2) method.

COPYRIGHT:
- Remove duplicate of BSD license.
2022-01-25 16:13:15 +00:00
Martijn Dekker
41ee12a527 Document history expansion and fix a few loose ends
src/cmd/ksh93/sh.1:
- Add a new section on history expansion mostly adapted from the
  "History substitution" section from the tcsh(1) man page. This
  has the standard BSD license which is already in the COPYRIGHT
  file. Inapplicable stuff was removed, some new stuff added.

src/cmd/ksh93/edit/hexpand.c,
src/cmd/ksh93/sh/io.c:
- Set '#' as the default history comment character as on bash;
  no longer disable it by default.
- Add the 'a' modifier as a synonym for 'g', as on bash.
- Remove pointless static keyword from np variable; since the
  value from previous calls is never used it can just be local.
- Use NV_NOADD flag with nv_open() to avoid pointlessly creating
  the node if the variable doesn't exist yet.
- Fix a bug in history expansion where the 'p' modifier had no
  effect if the 'histverify' option is on.
  Reproducer:
    $ set -H -o histv
    $ true a b c
    $ !!:p
    $ true a b c▁  <= instead of printed, the line is re-edited
  Expected:
    $ set -H -o histv
    $ true a b c
    $ !!:p
    true a b c
    $ ▁
  This is fixed by making 'p' remove the HIST_EVENT bit from the
  returned flag bits in hist_expand(), leaving only the HIST_PRINT
  flag, then adding special handling for this case to slowread()
  in io.c (print the line, then instead of executing it, continue
  and read the next line).
2022-01-25 03:45:47 +00:00
Martijn Dekker
2c4b05b4f8 tie up standards macros loose ends (re: 289dd46c)
src/lib/libast/features/standards:
- Do not emit #defines for the typ u_long test which is only used
  as a heuristic in subsequent tests in this file. (Note that 'set'
  can set and unset any iffe command-line --option at runtime.)
- Remove definition of _ISOC99_SOURCE macro. This is another old
  GNU thing; feature_test_macros(7) says invoking the compiler with
  the option -std=c99 has the same effect. But modern GCC has C11
  with GNU extensions as the default, which is fine. If a
  particular standard is desired, pass a -std=... flag in $CC.

src/cmd/ksh93/features/rlimits:
- Remove overlooked Linux *64* types/functions hackery.
  After defining standards macros it caused a build failure
  on at least one version of Void Linux (but not 5.15.14_1).
  Thanks to @JohnoKing for the report.

src/cmd/ksh93/sh/subshell.c,
src/lib/libdll/dllnext.c:
- Remove now-redundant local definitions of _GNU_SOURCE and
  __EXTENSIONS__ macros.

src/cmd/ksh93/tests/builtins.sh:
- Fix broken sed invocation (re: 41829efa).
2022-01-20 05:50:00 +00:00
Martijn Dekker
41829efa06 Various minor cleanups and fixes
The more notable ones are:

src/lib/libast/features/standards:
- Do not redefine _GNU_SOURCE and _FILE_OFFSET_BITS if already
  defined from $CCFLAGS. Thanks to @hyanias for the heads-up.
  (re: 289dd46c)

src/cmd/ksh93/data/builtins.c,
src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/sh/args.c,
src/cmd/ksh93/sh/name.c:
- Remove -T test code activation option. It was basically unused.
  The only thing it did was intentionally introduce a memory leak
  in table_unset() if the 4th bit in the option argument was set.
  A search in ast-open-history reveals a few more trivial test uses
  that were later deleted, but nothing interesting.

src/cmd/ksh93/tests/{basic,path}.sh:
- Skip a couple of tests on AIX avoid hangs, at least one of which
  is not ksh's fault. Thanks to @HansH111 for the report.

src/cmd/ksh93/tests/builtins.sh:
- Change one awk use to a more portable sed invocation to placate
  systems with ancient awk commands, such as AIX. (re: de795e1f)
2022-01-20 00:54:42 +00:00
Martijn Dekker
e569f23ef9 bump internal libast version; various minor cleanups
These are minor things I accumulated over the last month or so.

Notable changes:

src/lib/libast/features/api,
src/lib/libast/misc/state.c,
src/lib/libast/comp/conf.tab,
src/cmd/ksh93/include/defs.h:
- Bump internal libast version to 20220101L. We've made a few
  additions to the API, at least pathicase (see 71934570, ca3ec200)
  and astconf_long (see c2ac69b2), so this should have been done
  already. This also updates '/opt/ast/bin/getconf _AST_VERSION'.
- Use AST_VERSION instead of outdated _AST_VERSION.
- In state.c, use AST_VERSION instead of hardcoding the version.

src/cmd/ksh93/sh/xec.c:
- Remove 'restorefd' variable, unused as of 42becab6.
- Remove 'cmdrecurse' function and SH_RUNPROG macro; this was once
  used by a few libcmd commands, but ast-open-archive reveals it's
  unused as of ast 1999-12-25.

src/cmd/ksh93/sh/*.c:
- Where available, use e_dot instead of "." for consistency; it is
  defined as an extern so we might as well use it.

src/cmd/ksh93/tests/*.sh:
- When reporting signal names in fails, include the SIG prefix.
- Fix a broken process hang test in subshell.sh.

src/lib/libast/man/sfdisc.3:
- Removed. The interfaces described here never made it out of AT&T;
  they do not exist in any libast version in ast-open-archive.
  Resolves: https://github.com/ksh93/ksh/issues/426
2022-01-14 19:55:35 +00:00
Johnothan King
07fc64f52b Fix use after free bug in discipline functions (#424)
This fixes one of the ASan failures in the variables.sh regression
tests. Explanation from <https://github.com/att/ast/issues/1268>:

> The problem is caused by this block of code freeing the Namfun_t*
> (via the call to chktfree()):
> 307bc3ed/src/cmd/ksh93/sh/nvdisc.c (L570-L577)
>> 570  else
>> 571  {
>> 572          struct blocked *bp;
>> 573          action = vp->disc[type];
>> 574          vp->disc[type] = 0;
>> 575          if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN))
>> 576                  chktfree(np,vp);
>> 577  }
> That invalidates the value stored in vp which is dereferenced here:
> 307bc3ed/src/cmd/ksh93/sh/nvdisc.c (L411-L421)
>> 419          unblock(bp,type);
>> 420          if(!vp->disc[type])
>> 421                  chktfree(np,vp);

ksh2020 commit:
df1e8165

src/cmd/ksh93/sh/nvdisc.c:
- Block nv_setdisc from freeing the memory associated with the vp pointer.
2022-01-14 19:51:24 +00:00
Johnothan King
307bc3edce time: Fix precision bug in times(3) fallback (#425)
In the times(3) fallback for the time keyword (which can be enabled
in xec.c by undefining _lib_getrusage and timeofday), ksh will
print the obtained time incorrectly if TIMEFORMAT is set to use a
precision level of three:
   $ TIMEFORMAT=$'\nreal\t%3lR'
   $ time sleep .080
   real 0m00.008s  # Should be '00.080s'
This commit corrects that issue by using 10^precision to get the
correct fractional scaling. Note that the fallback still doesn't
support a true precision level of three (times(3) alone doesn't
support it), so this in effect pads a zero to the end of the output
when the precision level is three.

Additional change to tests/builtins.sh:
- While fixing the above issue I found out that ksh93v- broke
  support for passing microseconds to the sleep builtin in the form
  of <num>U. I've added a regression test for that bug to ensure it
  isn't backported to ksh93u+m by accident.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-01-13 12:25:22 +00:00
Martijn Dekker
b509e92241 edit: do not enable multiline mode with no editor active
If neither gmacs/emacs nor vi are active, the multiline mode should
not be enabled even if the multiline option is on. Doing so can
cause inconsistent behaviour, particularly in multibyte locales
where, if the shell is compiled with SHOPT_RAWONLY (as is default),
the no-editor mode is actually handled by vi.c.

Also, the new --histreedit and --histverify options only work in
the emacs or vi editors, or in no-editor mode when handled by vi.
Which means they cannot ever work if neither emacs or vi were
compiled in (i.e. SHOPT_ESH and SHOPT_VSH were both disabled).
In that case, there's no point in compiling in those options.
Come to think of it, the same applies to the multiline option.

All changed files:
- Update SHOPT_ESH/SHOPT_VSH preprocessor directives as per above.

src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/include/shell.h:
- Move definitions of history expansion-related options to shell.h,
  which is where all the other shell options are defined.
2022-01-12 20:39:05 +00:00
Martijn Dekker
2d4a787564 Fix comsub hang on subshell fork (re: 090b65e7)
The referenced commit introduced a bug that caused command
substitutions to hang, writing infinite zero bytes, when
redirecting standard output on a built-in comand that forks the
command substitution subshell.

The bug was caused by removing the fork when redirecting standard
output in a non-permanent manner. However, simply reintroducing the
fork causes multiple regressions that we had fixed in the meantime.

Thankfully, it looks like this forking workaround is only necessary
when redirecting the output of built-ins. It appears that moving
workaround from io.c to the built-ins handling code in sh_exec() in
xec.c, right before calling sh_redirect(), allows reintroducing the
forking workaround for non-permanent redirections without causing
other regressions.

It would be better if the underlying cause of the hang were fixed
so the workaround becomes unnecessary, but I don't think that is
going to happen any time soon (AT&T didn't manage, either).

src/cmd/ksh93/sh/io.c: sh_redirect():
- Remove forking workaround for redirecting stdout in a comsub.

src/cmd/ksh93/sh/xec.c: sh_exec(): TCOM: built-ins handling code:
- Reimplement the workaround here.

Resolves: https://github.com/ksh93/ksh/issues/416
2022-01-12 20:30:20 +00:00
Martijn Dekker
f711da9081 Make process substitutions work on Haiku
On Haiku:

    # /bin/cat <(echo hi)	   # no redirection
    cat: /tmp/ksh.f29pd8f: No such file or directory

Whereas this works fine:

    # /bin/cat < <(echo hi)	   # with redirection
    hi
    # /opt/ast/bin/cat <(echo hi)  # no redirection; use built-in
    hi

Haiku does not have /dev/fd, so uses the FIFO (named pipe) fallback
mechanism. See also: c3eac977

Analysis: In the TFORK part of sh_exec(), forked branch (child),
the FIFO (sh.fifo) is unlinked immediately after opening it. This
is not a problem if the process substitution is used in combination
with a redirection, but if not, then the FIFO is passed on to the
command as a file name argument. This creates a race condition: ksh
was counting on the external 'cat' command opening the FIFO before
the child could unlink it. Whether that race is won depends on
operating system implementation details. When invoking an external
command on Haiku, the race is lost.

src/cmd/ksh93/sh/xec.c: sh_exec(): TFORK: child branch:
- Delay unlinking the FIFO until after executing the process
  substitution, when we're about to exit from the child process.
2022-01-12 20:30:02 +00:00
Johnothan King
ca5803419b Fix various typos, man page issues and improve the documentation (#415)
This commit makes various different improvements to the documentation:
- sh.1: Backported (with changes) mandoc warning fixes from ksh2020
  for the ksh93(1) man page: <https://github.com/att/ast/pull/1406>
- Removed unnecessary spaces at the end of lines to fix a few other
  mandoc warnings.
- Fixed various typos and capitalization errors in the documentation.
- ANNOUNCE: Document the addition of the ${.sh.pid} variable
  (re: 9de65210).
- libast/man/str*: Update the man pages for the libast str* functions
  to improve how accurately each function is described.
- ksh93/README: Update regression test/compatibility notes to include
  OpenBSD 7.0, FreeBSD 13.0 and WSL running Ubuntu 20.04.
- Change a few places to store the return value from strlen in a
  size_t variable rather than signed int.
- comp/setlocale.c: To avoid confusion of two separate variables named
  lang, the function local variable has been renamed to langidx.
2022-01-07 16:17:55 +00:00
Johnothan King
d347ec0fc9 Allow ksh to compile on Haiku; implement SIGKILLTHR support (#408)
This commit implements the build fixes required to get ksh running on
Haiku. Note that while ksh does compile, it has a ton of regression test
failures on Haiku.

src/cmd/ksh93/data/signals.c,
src/lib/libast/features/signal.c:
- Add support for the SIGKILLTHR signal, which is supported by BeOS and
  Haiku.
- SIGINFO was missing an entry in the libast feature test, so add one
  (re: 658bba74).

src/cmd/ksh93/RELEASE:
- Add an entry noting that ksh now compiles on Haiku, albeit with many
  regression test failures.

src/cmd/ksh93/{include/terminal.h,sh/path.c}:
- Silence compiler warnings on Haiku.

src/lib/libast/features/mmap:
- The mmap feature test freezes on Haiku, so modify the test to fail
  immediately on that OS.

src/lib/libast/misc/signal.c:
- Avoid redefining the signal definition on Haiku to fix a compiler
  error.

src/lib/libast/features/nl_types:
- For some reason the nl_item typedef on Haiku doesn't work correctly.
  Work around that by creating the nl_item type in the libast nl_types
  feature test.
2022-01-07 16:16:42 +00:00
Martijn Dekker
b590a9f155 [shp cleanup 01..20] all the rest (re: 2d3ec8b6)
This combines 20 cleanup commits from the dev branch.

All changed files:
- Clean up pointer defererences to sh.
- Remove shp arguments from functions.

Other notable changes:

src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/sh/init.c:
- On second thought, get rid of the function version of
  sh_getinterp() as libshell ABI compatibility is moot. We've
  already been breaking that by reordering the sh struct, so there
  is no way it's going to work without recompiling.

src/cmd/ksh93/sh/name.c:
- De-obfuscate the relationship between nv_scan() and scanfilter().
  The former just calls the latter as a static function, there's no
  need to do that via a function pointer and void* type conversions.

src/cmd/ksh93/bltins/typeset.c,
src/cmd/ksh93/sh/name.c,
src/cmd/ksh93/sh/nvdisc.c:
- 'struct adata' and 'struct tdata', defined as local struct types
  in these files, need to have their first three fields in common,
  the first being a pointer to sh. This is because scanfilter() in
  name.c accesses these fields via a type conversion. So the sh
  field needed to be removed in all three at the same time.
  TODO: de-obfuscate: good practice definition via a header file.

src/cmd/ksh93/sh/path.c:
- Naming consistency: reserve the path_ function name prefix for
  externs and rename statics with that prefix.
- The default path was sometimes referred to as the standard path.
  To use one term, rename std_path to defpath and onstdpath() to
  ondefpath().
- De-obfuscate SHOPT_PFSH conditional code by only calling
  pf_execve() (was path_pfexecve()) if that is compiled in.

src/cmd/ksh93/include/streval.h,
src/cmd/ksh93/sh/streval.c:
- Rename extern strval() to arith_strval() for consistency.

src/cmd/ksh93/sh/string.c:
- Remove outdated/incorrect isxdigit() fallback; '#ifnded isxdigit'
  is not a correct test as isxdigit() is specified as a function.
  Plus, it's part of C89/C90 which we now require. (re: ac8991e5)

src/cmd/ksh93/sh/suid_exec.c:
- Replace an incorrect reference to shgd->current_pid with
  getpid(); it cannot work as (contrary to its misleading directory
  placement) suid_exec is an independent libast program with no
  link to ksh or libshell at all. However, no one noticed because
  this was in fallback code for ancient systems without
  setreuid(2). Since that standard function was specified in POSIX
  Issue 4 Version 2 from 1994, we should remove that fallback code
  sometime as part of another obsolete code cleanup operation to
  avoid further bit rot. (re: 843b546c)

src/cmd/ksh93/bltins/print.c: genformat():
- Remove preformat[] which was always empty and had no effect.

src/cmd/ksh93/shell.3:
- Minor copy-edit.
- Remove documentation for nonexistent sh.infile_name. A search
  through ast-open-archive[*] reveals this never existed at all.
- Document sh.savexit (== $?).

src/cmd/ksh93/shell.3,
src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/sh/init.c:
- Remove sh.gd/shgd; this is now unused and was never documented
  or exposed in the shell.h public interface.
- sh_sigcheck() was documented in shell.3 as taking no arguments
  whereas in the actual code it took a shp argument. I decided to
  go with the documentation.
- That leaves sh_parse() as the only documented function that still
  takes an shp argument. I'm just going to go ahead and remove it
  for consistency, reverting sh_parse() to its pre-2003 spec.
- Remove undocumented/unused sh_bltin_tree() function which simply
  returned sh.bltin_tree.
- Bump SH_VERSION to 20220106.
2022-01-07 16:16:31 +00:00
Martijn Dekker
01da863154 In the original ast code base, src/{cmd/nmake,lib/libast}/Makefile
(nmake makefiles) defined this macro:

	__OBSOLETE__ == $("6 months ago":@F=%(%Y0101)T)

This was used to automatically disable code after a period between
6 and 18 months, on 1st Jan of each year, in preprocessor
directives like:

	#if __OBSOLETE__ < 20080101
	// obsolete code here
	#endif

However, when compiling without nmake (as we do), this __OBSOLETE__
macro is not defined at all. And undefined macros evaluate to zero
in arithmetic comparisons, so all that obsolete code has been
getting compiled. Thankfully it doesn't seem to have done any harm,
but all that code was supposed to expire between 2008 and 2014.

src/lib/libast/disc/sfstrtmp.c:
- Removed. Was supposed to be a stub #if __OBSOLETE__ >= 20070101.

src/lib/libast/include/ast.h:
- Remove unused fmtbasell() macro (/* until 2014-01-01 */).

Other changed files:
- Remove __OBSOLETE__d code.
2022-01-07 15:57:46 +00:00
Johnothan King
f1627e2a8c Fix typeset -m crash under ASan and on OpenBSD (#412)
This fixes the use after free issue that caused typeset -m to crash on
older versions of OpenBSD and under ASan. The problem that was causing
the failure was that the ap pointer wasn't set to null after the memory
associated with it was freed. This commit backports a bugfix from
ksh93v- 2013-06-28 that sets ap to null before freeing the associated
memory and adds a check that makes sure ap is still a valid pointer
before calling array_unscope().

tests/types.sh changes:
- Avoid redirecting stderr to /dev/null, as this test shouldn't print
  anything to stderr.
- Apply error message improvement from
  https://github.com/ksh93/ksh/issues/231#issue-834252084.

tests/arrays.sh change:
- Apply error message improvement from
  https://github.com/ksh93/ksh/issues/229#issue-834240645 (re: 7c7fde75).

Resolves: https://github.com/ksh93/ksh/issues/231
2022-01-07 15:54:46 +00:00
Johnothan King
7c7fde75c8 Fix arrays.sh test failure under ASan (#411)
This backports a ksh2020 fix for an ASan heap-use-after-free error in
arrays.sh. The arrays regression tests were failing under ASan because
the ap pointer was used after the memory allocated to it was freed by
_nv_unset(). ksh2020 commit:
f1e5119e31
2022-01-07 15:53:10 +00:00
Martijn Dekker
d9fc61c022 init.c: upstream init.c.patch for CDE's dtksh
This upstreams the patch to init.c that is necessary to build dtksh
(graphical extensions for CDE, see <https://cdesktopenv.sf.net/>).
It has no effect when building regular ksh. Upstreaming it avoids
the need to keep updating it when changes to init.c are made.
2022-01-01 02:28:45 +00:00
Martijn Dekker
2d3ec8b67a [shp cleanup 00] Reunify the original sh state struct
As observed previously (see 3654ee73, 7e6bbf85, 79d19458), the ksh
93u+ codebase on which we rebased development was in a transition:
AT&T evidently wanted to make it possible to have several shell
interpreter states in the same process, which in theory would have
made it possible to start a complete new shell (not just a
subshell) without forking a new process.

This required transitioning from accessing the 'sh' state struct
directly to accessing it via pointers (usually but not always
called 'shp'), introducing a lot of bug-prone passing around of
those pointers via function arguments and other state structs.

Some of the original 'sh' struct was separated into a 'struct
shared' called 'shgd' a.k.a. 'sh.gd' (global data) instead; these
were global state variables that were going to be shared between
the different main shell environments sharing a process. Yet, for
some reason, that struct was allocated dynamically once at init
time, requiring yet another pointer to access it. <shrug>

None of this ever worked, because that transition was incomplete.
It was much further along in the ksh 93v- beta, but I don't think
it actually worked there either (not very much really did). So,
starting a new shell has always required starting a new process.

So, now that it's clear what they were trying to do, should we try
to make it work? I'm going to go with a firm "no" on that question.

Even non-forking (virtual) subshells, something quite a bit less
ambitious, were already an unmitigated nightmare of bugs. In 93u+m
we fixed a load of bugs related to those, but I'm sure there are
still many left. At the very least there are multiple memory leaks.

I think the ambition to go even further and have complete shells
running separate programs share a process, particularly given the
brittle and buggy state of the existing codebase, is evidence that
the AT&T team, in the final years, had well and truly lost the
ability to think "wait a minute, aren't we in over our heads here,
and why are we doing this again? Is this *actually* a feasible and
useful idea?"

In my view, having entirely separate programs share a process is a
*terrible*, horrible, no-good idea that takes us back to the bad
old days before Unix, when kernels and CPUs were unable to enforce
any memory access restrictions. Programmers are imperfect. If
you're going to run a new program, you need the kernel to enforce
the separation between programs, or you're just asking for memory
corruption and security holes. And that separation is enforced by
starting a new program in a new process. That's what processes are
*for*. And if you need *that* to be radically performance-optimised
then you're probably doing it wrong anyway.

(By the way, I would still argue the same for subshells, even after
we fixed many bugs in virtual subshells. But forking all subshells
would in fact cause many scripts to slow down, and the community
would surely revolt. <sigh>  Maybe I should make it a shell option
instead, so scripts can 'set -o subfork' for reliability.)

It is also unclear how they were going to make something like
'ulimit' work, which can only work in a separate process. There
was no sign of a mechanism to fork a separate program's shell
mid-execution like there is for subshells (sh_subfork()).

Anyway... I had already changed some code here and there to access
the sh state struct directly, but as of this commit I'm beginning
to properly undo this exercise in pointlessness. From now on, we're
exercising pointerlessness instead.

I'll do this in stages to make any problems introduced more
traceable. Stage 0 restores the full 'sh' state struct to its
former static glory and reverts 'shgd' as a separate entity.

src/cmd/ksh93/sh/defs.c,
src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/include/shell.h
src/cmd/ksh93/Mamfile::
- Move 'struct sh_scoped' and 'struct limits' from defs.h to
  shell.h as the sh struct will need their complete definitions.
- Get rid of 'struct shared' (shgd) in defs.h; its members are
  folded back into their original place, the main Shell_t struct
  (sh) in shell.h. There are no name conflicts.
- Get rid of the _SH_PRIVATE macro in defs.h. The members it
  defines are now defined normally in the main Shell_t struct (sh)
  in shell.h.
- To make this possible, move <history.h> and "fault.h" includes
  from defs.h to shell.h and update the Mamfile accordingly.
- Turn sh_getinterp() and shgd into macros that resolve to (&sh).
  This will allow the compiler to optimise out many pointer
  dereferences already.
- Keep extern sh_getinterp() for libshell ABI compatibility.

src/cmd/ksh93/sh/init.c:
- sh_init(): Do not calloc (sh_newof) the sh or shgd structs.
- sh_getinterp(): Keep function for libshell ABI compat.
2022-01-01 02:28:06 +00:00
Johnothan King
b425196958 Fix ASan heap-buffer-overflow when handling syntax errors (#402)
This commit backports a bugfix from ksh2020 to fix an ASan
heap-buffer-overflow error in one of the regression tests. See:
c57f7398
https://github.com/att/ast/issues/1261

This explanation comes from the linked issue:
> The poplevel() in this block of code is called when lp->lexd.lex_max
> is zero:
> bd94eb56/src/cmd/ksh93/sh/lex.c (L921-L925)
> Since poplevel() first decrements lp->lexd.lex_max then uses it as
> an index into lp->lexd.lex_match this causes the word before the
> start of that buffer to be accessed. The buffer is allocated here:
> bd94eb56/src/cmd/ksh93/sh/lex.c (L2210-L2218)

src/cmd/ksh93/sh/lex.c:
- Avoid calling poplevel() twice when handling syntax errors.
2021-12-28 17:53:35 +00:00
Martijn Dekker
e072e7c170 Fix crash in xtrace while processing here-document (re: d7cada7b)
Depending on the OS, the heredoc.sh regression tests, and possibly
others, still crashed with the -x option (xtrace) on.

Analysis: The lexer crashes in lex_advance(). Something has caused
an inconsistent lexer state, and it happened earlier on, so the
backtrace is useless for figuring out where that happened.

But I think I've found it. It's the sh_mactry() call here:

src/cmd/ksh93/sh/xec.c, lines 2800 to 2807 in f7213f03
2800:   if(!(cp=nv_getval(sh_scoped(shp,PS4NOD))))
2801:           cp = "+ ";
2802:   else
2803:   {
2804:           sh_offoption(SH_XTRACE);
2805:           cp = sh_mactry(shp,cp);
2806:           sh_onoption(SH_XTRACE);
2807:   }

sh_mactry() needs to parse the contents of $PS4 to perform
expansions and command substitutions in it, which involves the
lexer. If that happens in a here-document, the lexer is in the C
function call stack, in the middle of parsing the here-document.
Result: inconsistent lexer state. Solution: save and restore lexer
state in sh_mactry().

After this commit, all regression tests should pass with the
'-x'/'--xtrace' option in use, with no errors or crashes.

Note for backporters: this fix depends both on on d7cada7b and on
the consistency fix for the Lex_t type's size applied in a7ed5d9f.

src/cmd/ksh93/include/shlex.h:
- Cosmetic fix: remove a copied & pasted backslash. (re: a7ed5d9f)

src/cmd/ksh93/sh/macro.c: sh_mactry():
- Save and restore the lexer state before letting sh_mactrim()
  indirectly parse and execute code.

src/cmd/ksh93/tests/*.sh:
- Turn off xtrace in various command substitutions that contain
  2>&1 redirections, so that the xtrace output is not caught by
  the command substitutions, causing tests to fail incorrectly.
- Turn off xtrace for a few code blocks with 2>&1 redirections,
  stopping xtrace output from being written to standard output.

Resolves: https://github.com/ksh93/ksh/issues/306 (again)
2021-12-27 04:02:25 +00:00
Martijn Dekker
91a7c2e3e9 Fix crash/freeze upon interrupting command substitution with pipe
On some systems (at least Linux and macOS):

1. Run on a command line: t=$(sleep 10|while :; do :; done)
2. Press Ctrl+C in the first 10 seconds.
3. Execute any other command substitution. The shell crashes.

Analysis: Something in the job_wait() call in the sh_subshell()
restore routine may be interrupted by a signal such as SIGINT on
Linux and macOS. Exactly what that interruptible thing is remains
to be determined. In any case, since job_wait() was invoked after
sh_popcontext(), interrupting it caused the sh_subshell() restore
routine to be aborted, resulting in an inconsistent state of the
shell. The fix is to sh_popcontext() at a later stage instead.

src/cmd/ksh93/sh/subshell.c: sh_subshell():
- Rename struct checkpt buff to checkpoint because it's clearer.
- Move the sh_popcontext() call to near the end, just after
  decreasing the subshell level counters and restoring the global
  subshell data struct to its parent. This seems like a logical
  place for it and could allow other things to be interrupted, too.
- Get rid of the if(shp->subshell) because it is known that the
  value is > 0 at this point.
- The short exit routine run if the subshell forked now needs a new
  sh_popcontext() call, because this is handled before restoring
  the virtual subshell state.
- While we're here, do a little more detransitioning from all those
  pointless shp pointers.

Fixes: https://github.com/ksh93/ksh/issues/397
2021-12-27 03:49:41 +00:00
Johnothan King
feeb62d15f sh_close(): Set errno to EBADF for invalid file descriptors (#399)
The sh_close() function fails to set errno to EBADF when passed a
negative (invalid) file descriptor. This commit fixes the issue
by setting errno if the file descriptor is a negative value
(backported from ksh93v- 2012-08-24).
2021-12-27 03:48:38 +00:00
Martijn Dekker
a1f5c99204 INIT: remove proto, ratz (re: 46593a89, 6137b99a); major cleanup
This takes another step towards cleaning up the build system. We
now do not even pretend to be theoretically compatible with
pre-1989 K&R C compilers or with C++ compilers. In practice, this
had already been broken for many years due to bit rot.

Commit 46593a89 already removed the license handling enormity that
depended on proto, so now we can cleanly remove it altogether. But
we do need to leave some backwards compatibility stubs to keep the
build system compatible with older AST code; it should remain
possible to build older ksh versions with the current build system
(the bin/ and src/cmd/INIT/ directories) for testing purposes.

So as of now there is no more __MANGLE__d rubbish in your generated
header files. This is only about a quarter of a century overdue...

This commit also includes a huge amount of code cleanup to remove
thousands of unused K&R C fallbacks and other cruft, particularly
in libast. This code base should now be a little easier to
understand for people who are familiar with a modern(ish) C
standard.

ratz is now also removed; this was a standalone and simplified 2005
version of gunzip. As of 6137b99a, none of our code uses it, even
theoretically. And the real g(un)zip is now everywhere.

src/cmd/INIT/proto.c, src/cmd/INIT/ratz.c:
- Removed.

COPYRIGHT:
- Remove zlib license; this only applied to ratz.

bin/package, src/cmd/INIT/package.sh:
- Related cleanups.
- Unset LC_ALL before invoking a new shell, respecting the user's
  locale again and avoiding multibyte character corruption on the
  command line.

src/cmd/INIT/proto.sh:
- Add stub for backwards compatibility with Mamfiles that depend on
  proto. It does nothing but pass input without modification and is
  now installed as the new arch/*/bin/proto by src/cmd/INIT/Mamfile.

src/cmd/INIT/iffe.sh:
- Ignore the proto-related -e (--package) and -p (--prototyped)
  options; keep parsing them for backwards compatibility.
- Trim the macros passed to every test to their standard C
  versions, removing K&R C and C++ versions. These are now
  considered to be for backwards compatibility only.

src/cmd/INIT/iffe.tst:
- Remove proto(1) mangling code.
  By the way, iffe can be regression-tested as follows:
        $ bin/package use   # set up environment in a child shell
        $ regress src/cmd/INIT/iffe.tst
        $ exit              # leave package environment

src/cmd/INIT/make.probe, src/cmd/INIT/probe.win32:
- Remove code to handle C++.

src/lib/libast/features/common:
- As in iffe.sh above, trim macros designed for compatibility with
  C++ and ancient C compilers to their standard C versions and
  comment that they are for backwards compatibility with AST code.
  This is needed to keep all the old ast and ksh code compiling.

src/cmd/ksh93/sh/init.c,
src/cmd/ksh93/sh/name.c:
- Clarify libshell ABI compatibility function versions of macros.
  A "proto workaround" comment in the original code mislead me into
  thinking this had something to do with the removed proto(1), but
  it's unrelated. Call the workaround macro BYPASS_MACRO instead.

src/cmd/ksh93/include/defs.h:
- sh_sigcheck() macro: allow &sh as an argument: parenthesise shp.

src/cmd/ksh93/sh/nvtype.c:
- Remove unused nv_mkstruct() function. (re: d0a5cab1)

**/features/*:
- Remove obsolete iffe 'set prototyped' option.

**/Mamfile:
- Remove all references to the ast/prototyped.h header.
- Remove all use of the proto command. Simply copy instead.

*** 850-ish source files: ***
- Remove all '#pragma prototyped' directives.
- Remove all C++ compat code conditional upon defined(__cplusplus).
- Remove all use of the _ARG_ macro, which on standard C expands to
  its argument:
        #define _ARG_(x)        x
  (on K&R C, it expanded to nothing)
- Remove all use of _BEGIN_EXTERNS_ and _END_EXTERNS_ macros (empty
  on standard C; this was for C++ compatibility)
- Reduce all #if __STD_C (standard code) #else (K&R code) #endif
  blocks to the standard code only, without use of the macro.
- Same for _STD_ macro which seems to have had the same function.
- Change all instances of 'Void_t' to standard 'void'.
2021-12-24 07:05:22 +00:00