diff --git a/src/cmd/ksh93/Mamfile b/src/cmd/ksh93/Mamfile index f6e6ac8..9d72d87 100644 --- a/src/cmd/ksh93/Mamfile +++ b/src/cmd/ksh93/Mamfile @@ -220,30 +220,18 @@ make install done pmain.o generated make libshell.a archive prev shell.req - make cd_pwd.o - make bltins/cd_pwd.c - make include/test.h implicit - prev include/shtable.h implicit - make include/defs.h implicit - prev include/regress.h implicit dontcare - prev include/shtable.h - prev include/shell.h - prev ${PACKAGE_ast_INCLUDE}/endian.h - prev include/name.h - prev include/argnod.h implicit - prev ${PACKAGE_ast_INCLUDE}/cdt.h - prev ${PACKAGE_ast_INCLUDE}/error.h - prev ${PACKAGE_ast_INCLUDE}/sfio.h + make alarm.o + make bltins/alarm.c + make FEATURE/time implicit + prev features/time + exec - iffe ${IFFEFLAGS} -v -c "${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS}" ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libnsl} : run features/time + make ${PACKAGE_ast_INCLUDE}/times.h implicit + prev ${PACKAGE_ast_INCLUDE}/ast_time.h implicit prev ${PACKAGE_ast_INCLUDE}/ast.h - done include/defs.h - done include/test.h - make ${PACKAGE_ast_INCLUDE}/ls.h implicit - prev ${PACKAGE_ast_INCLUDE}/ast_mode.h implicit dontcare - prev ${PACKAGE_ast_INCLUDE}/ast_fs.h - prev ${PACKAGE_ast_INCLUDE}/ast_std.h - done ${PACKAGE_ast_INCLUDE}/ls.h + done ${PACKAGE_ast_INCLUDE}/times.h + done FEATURE/time generated make include/builtins.h implicit - prev include/shtable.h + prev include/shtable.h implicit make FEATURE/dynamic implicit prev features/dynamic exec - iffe ${IFFEFLAGS} -v -c "${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS}" ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libnsl} : run features/dynamic @@ -251,6 +239,36 @@ make install done FEATURE/dynamic generated prev ${PACKAGE_ast_INCLUDE}/option.h done include/builtins.h + prev ${PACKAGE_ast_INCLUDE}/error.h + make include/defs.h implicit + prev include/regress.h implicit + prev include/shtable.h + prev include/shell.h + prev ${PACKAGE_ast_INCLUDE}/endian.h + prev include/name.h + prev include/argnod.h implicit + prev ${PACKAGE_ast_INCLUDE}/cdt.h + prev FEATURE/externs + prev ${PACKAGE_ast_INCLUDE}/error.h + prev ${PACKAGE_ast_INCLUDE}/sfio.h + prev ${PACKAGE_ast_INCLUDE}/ast.h + done include/defs.h + prev shopt.h + done bltins/alarm.c + exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_API_ast=20100309 -DERROR_CONTEXT_T=Error_context_t -c bltins/alarm.c + done alarm.o generated + make cd_pwd.o + make bltins/cd_pwd.c + make include/test.h implicit + prev include/shtable.h + prev include/defs.h + done include/test.h + make ${PACKAGE_ast_INCLUDE}/ls.h implicit + prev ${PACKAGE_ast_INCLUDE}/ast_mode.h implicit + prev ${PACKAGE_ast_INCLUDE}/ast_fs.h + prev ${PACKAGE_ast_INCLUDE}/ast_std.h + done ${PACKAGE_ast_INCLUDE}/ls.h + prev include/builtins.h prev include/name.h make include/path.h implicit make FEATURE/acct implicit @@ -341,14 +359,7 @@ make install make misc.o make bltins/misc.c prev ${PACKAGE_ast_INCLUDE}/times.h - make FEATURE/time implicit - prev features/time - exec - iffe ${IFFEFLAGS} -v -c "${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS}" ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libnsl} : run features/time - make ${PACKAGE_ast_INCLUDE}/times.h implicit - prev ${PACKAGE_ast_INCLUDE}/ast_time.h implicit dontcare - prev ${PACKAGE_ast_INCLUDE}/ast.h - done ${PACKAGE_ast_INCLUDE}/times.h dontcare - done FEATURE/time generated + prev FEATURE/time prev FEATURE/locale make include/jobs.h implicit prev ${PACKAGE_ast_INCLUDE}/vmalloc.h @@ -1175,7 +1186,7 @@ make install done edit/hexpand.c exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_API_ast=20100309 -DERROR_CONTEXT_T=Error_context_t -c edit/hexpand.c done hexpand.o generated - exec - ${AR} rc libshell.a cd_pwd.o cflow.o enum.o getopts.o hist.o misc.o mkservice.o print.o read.o sleep.o trap.o test.o typeset.o ulimit.o umask.o whence.o main.o nvdisc.o nvtype.o arith.o args.o array.o completion.o defs.o edit.o expand.o regress.o fault.o fcin.o + exec - ${AR} rc libshell.a alarm.o cd_pwd.o cflow.o enum.o getopts.o hist.o misc.o mkservice.o print.o read.o sleep.o trap.o test.o typeset.o ulimit.o umask.o whence.o main.o nvdisc.o nvtype.o arith.o args.o array.o completion.o defs.o edit.o expand.o regress.o fault.o fcin.o exec - ${AR} rc libshell.a history.o init.o io.o jobs.o lex.o macro.o name.o nvtree.o parse.o path.o string.o streval.o subshell.o tdump.o timers.o trestore.o waitevent.o xec.o limits.o msg.o strdata.o testops.o keywords.o options.o signals.o aliases.o builtins.o variables.o lexstates.o emacs.o vi.o hexpand.o exec - (ranlib libshell.a) >/dev/null 2>&1 || true done libshell.a generated diff --git a/src/cmd/ksh93/bltins/alarm.c b/src/cmd/ksh93/bltins/alarm.c new file mode 100644 index 0000000..f31bed7 --- /dev/null +++ b/src/cmd/ksh93/bltins/alarm.c @@ -0,0 +1,277 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2012 AT&T Intellectual Property * +* Copyright (c) 2020-2023 Contributors to ksh 93u+m * +* and is licensed under the * +* Eclipse Public License, Version 2.0 * +* * +* A copy of the License is available at * +* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * +* (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d) * +* * +* David Korn * +* Martijn Dekker * +* Johnothan King * +* * +***********************************************************************/ +/* + * alarm [-r] [varname [+]when] + * + * David Korn + * AT&T Labs + * + */ + +/* + * TODO: 2014 email from David Korn cited at : + * + * > I never documented the alarm builtin because it is problematic. The + * > problem is that traps can't safely be handled asynchronously. What should + * > happen is that the trap is marked for execution (sh.trapnote) and run after + * > the current command completes. The time trap should wake up the shell if + * > it is blocked and it should return and then handle the trap. + */ + +#include "shopt.h" +#include "defs.h" +#include +#include "builtins.h" +#include "FEATURE/time" + +#define R_FLAG 1 +#define L_FLAG 2 + +struct tevent +{ + Namfun_t fun; + Namval_t *node; + Namval_t *action; + struct tevent *next; + long milli; + int flags; + void *timeout; +}; + +static const char ALARM[] = "alarm"; + +static void trap_timeout(void*); + +/* + * insert timeout item on current given list in sorted order + */ +static void *time_add(struct tevent *item, void *list) +{ + struct tevent *tp = (struct tevent*)list; + if(!tp || item->milli < tp->milli) + { + item->next = tp; + list = item; + } + else + { + while(tp->next && item->milli > tp->next->milli) + tp = tp->next; + item->next = tp->next; + tp->next = item; + } + tp = item; + tp->timeout = sh_timeradd(tp->milli,tp->flags&R_FLAG,trap_timeout,tp); + return list; +} + +/* + * delete timeout item from current given list, delete timer + */ +static void *time_delete(struct tevent *item, void *list) +{ + struct tevent *tp = (struct tevent*)list; + if(item==tp) + list = tp->next; + else + { + while(tp && tp->next != item) + tp = tp->next; + if(tp) + tp->next = item->next; + } + if(item->timeout) + sh_timerdel(item->timeout); + return list; +} + +static void print_alarms(void *list) +{ + struct tevent *tp = (struct tevent*)list; + while(tp) + { + if(tp->timeout) + { + char *name = nv_name(tp->node); + if(tp->flags&R_FLAG) + { + double d = tp->milli; + sfprintf(sfstdout,e_alrm1,name,d/1000.); + } + else + sfprintf(sfstdout,e_alrm2,name,nv_getnum(tp->node)); + } + tp = tp->next; + } +} + +static void trap_timeout(void* handle) +{ + struct tevent *tp = (struct tevent*)handle; + sh.trapnote |= SH_SIGALRM; + if(!(tp->flags&R_FLAG)) + tp->timeout = 0; + tp->flags |= L_FLAG; + sh.sigflag[SIGALRM] |= SH_SIGALRM; + if(sh_isstate(SH_TTYWAIT)) + sh_timetraps(); +} + +void sh_timetraps(void) +{ + struct tevent *tp, *tpnext; + struct tevent *tptop; + while(1) + { + sh.sigflag[SIGALRM] &= ~SH_SIGALRM; + tptop= (struct tevent*)sh.st.timetrap; + for(tp=tptop;tp;tp=tpnext) + { + tpnext = tp->next; + if(tp->flags&L_FLAG) + { + tp->flags &= ~L_FLAG; + if(tp->action) + sh_fun(tp->action,tp->node,NULL); + tp->flags &= ~L_FLAG; + if(!tp->flags) + nv_unset(tp->node); + } + } + if(!(sh.sigflag[SIGALRM]&SH_SIGALRM)) + break; + } +} + + +/* + * This trap function catches "alarm" actions only + */ +static char *setdisc(Namval_t *np, const char *event, Namval_t* action, Namfun_t *fp) +{ + struct tevent *tp = (struct tevent*)fp; + if(!event) + return action ? Empty : (char*)ALARM; + if(strcmp(event,ALARM)!=0) + { + /* try the next level */ + return nv_setdisc(np, event, action, fp); + } + if(action==np) + action = tp->action; + else + tp->action = action; + return action ? (char*)action : Empty; +} + +/* + * catch assignments and set alarm traps + */ +static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp) +{ + struct tevent *tp = (struct tevent*)fp; + double d; + if(val) + { + double now; +#ifdef timeofday + struct timeval tmp; + timeofday(&tmp); + now = tmp.tv_sec + 1.e-6*tmp.tv_usec; +#else + now = (double)time(NULL); +#endif /* timeofday */ + nv_putv(np,val,flag,fp); + d = nv_getnum(np); + if(*val=='+') + { + double x = d + now; + nv_putv(np,(char*)&x,NV_INTEGER|NV_DOUBLE,fp); + } + else + d -= now; + tp->milli = 1000*(d+.0005); + if(tp->timeout) + sh.st.timetrap = time_delete(tp,sh.st.timetrap); + if(tp->milli > 0) + sh.st.timetrap = time_add(tp,sh.st.timetrap); + } + else + { + tp = (struct tevent*)nv_stack(np, NULL); + sh.st.timetrap = time_delete(tp,sh.st.timetrap); + nv_unset(np); + free(fp); + } +} + +static const Namdisc_t alarmdisc = +{ + sizeof(struct tevent), + putval, + 0, + 0, + setdisc, +}; + +int b_alarm(int argc,char *argv[],Shbltin_t *context) +{ + int n,rflag=0; + Namval_t *np; + struct tevent *tp; + while (n = optget(argv, sh_optalarm)) switch (n) + { + case 'r': + rflag = R_FLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + UNREACHABLE(); + } + argc -= opt_info.index; + argv += opt_info.index; + if(error_info.errors) + { + errormsg(SH_DICT,ERROR_usage(2),optusage(NULL)); + UNREACHABLE(); + } + if(argc==0) + { + print_alarms(sh.st.timetrap); + return 0; + } + if(argc!=2) + { + errormsg(SH_DICT,ERROR_usage(2),optusage(NULL)); + UNREACHABLE(); + } + np = nv_open(argv[0],sh.var_tree,NV_NOARRAY|NV_VARNAME); + if(!nv_isnull(np)) + nv_unset(np); + nv_setattr(np, NV_DOUBLE); + tp = sh_newof(NULL,struct tevent,1,0); + tp->fun.disc = &alarmdisc; + tp->flags = rflag; + tp->node = np; + nv_stack(np,(Namfun_t*)tp); + nv_putval(np, argv[1], 0); + return 0; +} diff --git a/src/cmd/ksh93/bltins/sleep.c b/src/cmd/ksh93/bltins/sleep.c index b34e62d..3bb55b1 100644 --- a/src/cmd/ksh93/bltins/sleep.c +++ b/src/cmd/ksh93/bltins/sleep.c @@ -127,6 +127,8 @@ skip: if(tloc < (now=time(NULL))) break; d = (double)(tloc-now); + if(sh.sigflag[SIGALRM]&SH_SIGTRAP) + sh_timetraps(); } return 0; } diff --git a/src/cmd/ksh93/data/builtins.c b/src/cmd/ksh93/data/builtins.c index 760b7ce..22ea156 100644 --- a/src/cmd/ksh93/data/builtins.c +++ b/src/cmd/ksh93/data/builtins.c @@ -116,6 +116,7 @@ const struct shtable3 shtab_builtins[] = "pwd", NV_BLTIN|BLT_ENV, bltin(pwd), "read", NV_BLTIN|BLT_ENV, bltin(read), "sleep", NV_BLTIN, bltin(sleep), + "alarm", NV_BLTIN|BLT_ENV, bltin(alarm), "times", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(times), "ulimit", NV_BLTIN|BLT_ENV, bltin(ulimit), "umask", NV_BLTIN|BLT_ENV, bltin(umask), @@ -2170,6 +2171,8 @@ const char sh_optwhence[] = ; +const char e_alrm1[] = "alarm -r %s +%.3g\n"; +const char e_alrm2[] = "alarm %s %.3f\n"; const char e_baddisc[] = "%s: invalid discipline function"; const char e_nofork[] = "cannot fork"; const char e_nosignal[] = "%s: unknown signal name"; diff --git a/src/cmd/ksh93/include/builtins.h b/src/cmd/ksh93/include/builtins.h index 34c697c..f6b3f59 100644 --- a/src/cmd/ksh93/include/builtins.h +++ b/src/cmd/ksh93/include/builtins.h @@ -115,6 +115,8 @@ extern int b_times(int, char*[],Shbltin_t*); extern short b_enum_nelem(Namfun_t*); +extern const char e_alrm1[]; +extern const char e_alrm2[]; extern const char e_badfun[]; extern const char e_baddisc[]; extern const char e_nofork[]; diff --git a/src/cmd/ksh93/include/defs.h b/src/cmd/ksh93/include/defs.h index 306ea7e..b5bfb67 100644 --- a/src/cmd/ksh93/include/defs.h +++ b/src/cmd/ksh93/include/defs.h @@ -143,6 +143,7 @@ extern void sh_subjobcheck(pid_t); extern int sh_subsavefd(int); extern void sh_subtmpfile(void); extern char *sh_substitute(const char*,const char*,char*); +extern void sh_timetraps(void); extern const char *_sh_translate(const char*); extern int sh_trace(char*[],int); extern void sh_trim(char*); diff --git a/src/cmd/ksh93/include/fault.h b/src/cmd/ksh93/include/fault.h index b57a0ab..7750f80 100644 --- a/src/cmd/ksh93/include/fault.h +++ b/src/cmd/ksh93/include/fault.h @@ -58,6 +58,7 @@ typedef void (*SH_SIGTYPE)(int,void(*)(int)); #define SH_SIGIGNORE 040 /* default is ignore signal */ #define SH_SIGINTERACTIVE 0100 /* handle interactive specially */ #define SH_SIGTSTP 0200 /* tstp signal received */ +#define SH_SIGALRM 0200 /* timer alarm received */ #define SH_SIGTERM SH_SIGOFF /* term signal received */ #define SH_SIGRUNTIME 0400 /* runtime value */ diff --git a/src/cmd/ksh93/include/shell.h b/src/cmd/ksh93/include/shell.h index 57c402d..9794995 100644 --- a/src/cmd/ksh93/include/shell.h +++ b/src/cmd/ksh93/include/shell.h @@ -211,6 +211,7 @@ struct sh_scoped char **otrap; /* save parent pseudosignals for v=$(trap) */ char **trapcom; /* EXIT and signals */ char **otrapcom; /* save parent EXIT and signals for v=$(trap) */ + void *timetrap; struct Ufunction *real_fun; /* current 'function name' function */ int repl_index; char *repl_arg; diff --git a/src/cmd/ksh93/sh/fault.c b/src/cmd/ksh93/sh/fault.c index 3409a6c..510bf56 100644 --- a/src/cmd/ksh93/sh/fault.c +++ b/src/cmd/ksh93/sh/fault.c @@ -447,6 +447,8 @@ void sh_chktrap(void) sh_exit(sh.exitval); } } + if(sh.sigflag[SIGALRM]&SH_SIGALRM) + sh_timetraps(); #if SHOPT_BGX if((sh.sigflag[SIGCHLD]&SH_SIGTRAP) && sh.st.trapcom[SIGCHLD]) job_chldtrap(1); diff --git a/src/cmd/ksh93/sh/jobs.c b/src/cmd/ksh93/sh/jobs.c index 116ae3b..be333b2 100644 --- a/src/cmd/ksh93/sh/jobs.c +++ b/src/cmd/ksh93/sh/jobs.c @@ -1466,6 +1466,8 @@ int job_wait(pid_t pid) continue; if(nochild) break; + if(sh.sigflag[SIGALRM]&SH_SIGTRAP) + sh_timetraps(); if((intr && sh.trapnote) || (pid==1 && !intr)) break; } diff --git a/src/cmd/ksh93/tests/builtins.sh b/src/cmd/ksh93/tests/builtins.sh index 83b4086..92d2708 100755 --- a/src/cmd/ksh93/tests/builtins.sh +++ b/src/cmd/ksh93/tests/builtins.sh @@ -1222,6 +1222,28 @@ function test_usage done 3< <(builtin) }; test_usage +# ====== +# The 'alarm' builtin could make 'read' crash due to IFS table corruption caused by unsafe asynchronous execution. +# https://bugzilla.redhat.com/1176670 +if (builtin alarm) 2>/dev/null +then got=$( { "$SHELL" -c ' + builtin alarm + alarm -r alarm_handler +.005 + i=0 + function alarm_handler.alarm + { + let "(++i) > 20" && exit + } + while :; do + echo cargo,odds and ends,jetsam,junk,wreckage,castoffs,sea-drift + done | while IFS="," read arg1 arg2 arg3 arg4 junk; do + : + done + '; } 2>&1) + ((!(e = $?))) || err_exit 'crash with alarm and IFS' \ + "(got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"), $(printf %q "$got"))" +fi + # ====== # Verify that the POSIX 'test' builtin exits with status 2 when given an invalid binary operator. for operator in '===' ']]' @@ -1581,7 +1603,7 @@ let Errors+=$? # Most built-ins should handle --version while IFS= read -r bltin <&3 do case $bltin in - echo | test | true | false | \[ | : | catclose | catgets | catopen | Dt* | _Dt* | X* | login | newgrp ) + alarm | echo | test | true | false | \[ | : | catclose | catgets | catopen | Dt* | _Dt* | X* | login | newgrp ) continue ;; fc | hist ) ((SHOPT_SCRIPTONLY)) && continue ;;