RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1283329 - RFC: ksh problem when restoring realloc'ed pointer
Summary: RFC: ksh problem when restoring realloc'ed pointer
Keywords:
Status: CLOSED CANTFIX
Alias: None
Product: Red Hat Enterprise Linux 6
Classification: Red Hat
Component: ksh
Version: 6.8
Hardware: All
OS: All
unspecified
medium
Target Milestone: rc
: ---
Assignee: Siteshwar Vashisht
QA Contact: BaseOS QE - Apps
URL:
Whiteboard:
Depends On:
Blocks: 1269194
TreeView+ depends on / blocked
 
Reported: 2015-11-18 17:45 UTC by Paulo Andrade
Modified: 2019-10-10 10:31 UTC (History)
0 users

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2017-06-13 08:03:58 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)

Description Paulo Andrade 2015-11-18 17:45:23 UTC
I have a coredump where the only explanation I can
infer is a crash caused by restoring a pointer, in the
middle of a buffer that had become dangling, due to a
realloc.

  The code is in:

src/cmd/ksh93/sh/xec.c:sh_exec()

---8<---
int sh_exec(register const Shnode_t *t, int flags)
{
	register Shell_t	*shp = sh_getinterp();
	Stk_t			*stkp = shp->stk;
	int			unpipe=0;
	sh_sigcheck(shp);
	if(t && !shp->st.execbrk && !sh_isoption(SH_NOEXEC))
	{
		register int 	type = flags;
		register char	*com0 = 0;
		int 		errorflg = (type&sh_state(SH_ERREXIT))|OPTIMIZE;
		int 		execflg = (type&sh_state(SH_NOFORK));
		int 		execflg2 = (type&sh_state(SH_FORKED));
		int 		mainloop = (type&sh_state(SH_INTERACTIVE));
#if SHOPT_AMP || SHOPT_SPAWN
		int		ntflag = (type&sh_state(SH_NTFORK));
#else
		int		ntflag = 0;
#endif
		int		topfd = shp->topfd;
		char 		*sav=stkptr(stkp,0);
---8<---

  As you can see, the "sav" variable is local, and keeps
the return value of the stkptr call.
For more details:
#define	stkptr(sp,n)	((char*)((sp)->_data)+(n))

  Later, that value is restored, if not in "optimized" mode:
---8<---
#if SHOPT_COSHELL
		if(!shp->inpool && !(OPTIMIZE))
#else
		if(!(OPTIMIZE))
#endif /* SHOPT_COSHELL */
		{
			if(sav != stkptr(stkp,0))
				stkset(stkp,sav,0);
			else if(stktell(stkp))
				stkseek(stkp,0);
		}
---8<---

  The problem is that, in several constructs there are calls
in the pattern:
					struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt));

and stkalloc may call stkgrow, that may call realloc, and that
causes the local variable "sav" to possibly point to dangling
memory, if the realloc causes the base pointer to move.

  A kind of "blind" fix would be to save an offset to the base
of the stack, and restore it, e.g.:

    int offset=stktell(stkp);
....
    stkseek(stkp,offset);

but that may have side effects I did not consider, e.g. due to
the possibly being cast to "struct frame" and/or other uses.
Also, it is interesting to tell that, stkset will "reset" the
stack if the restored pointer is not in any frame of the stack,
when it is cast to "struct frame".

  The cause of the crash is that it looks as if there was an
unbalanced sh_pushcontext() ... sh_popcontext() sequence,
causing the sh_popcontext to have a NULL sigjump_buf, as
effectively, one of the sh_popcontext calls did:

shp->jmplist = shp->jmplist.prev;

but the prev field was NULL.


  I could not so far create a reproducer, only have the coredump
that leads to the logic described above, and that following the
code, is very likely the reason, as it does not have any kind
of protection for updating the "sav" pointer if the shp->stk
pointer is changed due to a realloc.

  Looking at the backtrace, it also appears to have "sav"
pointers out of the stack. Only frame 0, where the crash
happens, has sav inside the stack. It crashes because
shp->jmplist is NULL.

(gdb) frame 0
#0  sh_exec (t=0x7f34e32327f0, flags=516) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:2306
2306				int  jmpval = ((struct checkpt*)shp->jmplist)->mode;
(gdb) p (sav >= shp->stk._data) & (sav < shp->stk._endb)
$17 = 1
(gdb) frame 1
#1  0x000000000045952b in sh_exec (t=0x7f34e3232ce0, flags=516) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:2256
2256					sh_exec(t->lst.lstlef,errorflg|OPTIMIZE);
(gdb) p (sav >= shp->stk._data) & (sav < shp->stk._endb)
$18 = 0
(gdb) frame 2
#2  0x000000000045baa0 in sh_exec (t=0x7f34e3231020, flags=516) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:2573
2573					sh_exec(t->if_.thtre,flags);
(gdb) p (sav >= shp->stk._data) & (sav < shp->stk._endb)
$19 = 0
(gdb) frame 3
#3  0x000000000045baa0 in sh_exec (t=0x7f34e3230ea0, flags=516) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:2573
2573					sh_exec(t->if_.thtre,flags);
(gdb) p (sav >= shp->stk._data) & (sav < shp->stk._endb)
$20 = 0
(gdb) frame 4
#4  0x000000000045952b in sh_exec (t=0x7f34e3233e60, flags=516) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:2256
2256					sh_exec(t->lst.lstlef,errorflg|OPTIMIZE);
(gdb) p (sav >= shp->stk._data) & (sav < shp->stk._endb)
$21 = 0
(gdb) frame 5
#5  0x00000000004588f9 in sh_exec (t=0x7f34e3230280, flags=4) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:2401
2401					sh_exec(t->for_.fortre,flag);
(gdb) p (sav >= shp->stk._data) & (sav < shp->stk._endb)
$22 = 0
(gdb) frame 6
#6  0x000000000045952b in sh_exec (t=0x7f34e3234050, flags=6) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:2256
2256					sh_exec(t->lst.lstlef,errorflg|OPTIMIZE);
(gdb) p (sav >= shp->stk._data) & (sav < shp->stk._endb)
$23 = 0

(Used & instead of && due to apparent gdb issue printing the expression)

Comment 1 Paulo Andrade 2015-11-18 18:00:52 UTC
  I believe this is related to some other reports, where ksh
had just siglongjmp'ed to an "impossible" address, what ends
up with registers clobbered, and no way to figure out from
where it did jump.

Comment 3 Paulo Andrade 2015-11-19 03:09:41 UTC
  From my understanding, the OPTIMIZE_FLAG value is
actually a "hidden" way to prevent the execution of

		if(!(OPTIMIZE))
#endif /* SHOPT_COSHELL */
		{
			if(sav != stkptr(stkp,0))
				stkset(stkp,sav,0);
			else if(stktell(stkp))
				stkseek(stkp,0);
		}


where OPTIMIZE is defined as
#   define OPTIMIZE		(flags&OPTIMIZE_FLAG)
and "force" it to only call sktset on the bottom of
the stack of called shell builtins or functions,
having the effect of hiding the bad pointer passed
to sktset. And, if a bad pointer is passed, it just
resets the stack; and, if at the bottom, it has the
effect of not crashing and continuing to work "correctly",
besides having earlier a crash condition.
  So, a proper test may require to have "flags&OPTIMIZE_FLAG"
always unset, what may require a special ksh build.


Note You need to log in before you can comment on or make changes to this bug.