ARM semihosting SYS_GET_CMDLINE does not return arguments

Bug #673613 reported by Wolfgang Schildbach
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
QEMU
Fix Released
Undecided
Unassigned

Bug Description

I have downloaded version 0.13.0 of the code, configured with: './configure' '--prefix=/tmp/qemu-0.13.0' '--interp-prefix=/tmp/qemu-0.13.0/usr/local/gnemul/qemu-%M' '--target-list=arm-softmmu arm-linux-user armeb-linux-user'

and built using gcc version 4.3.2 (Debian 4.3.2-1.1). Execution environment is Debian, kernel 2.6.26-2-686.

I am running a barebone helloworld.c which echoes its commandline arguments, compiled with ADS1.2 from ARM. The arguments never get echoed back.

I have found the culprit in the source code, lines 3020 and 3022 of linux-user/main.c which free target_argv[]. However, loader_exec(), which is called a couple lines above, records the pointer to target_argv[]. So, when the data is accessed in arm-semi.c, it is actually trying to load from memory that has been free()d already.

This bug manifests itself for baremetal simulation, but I suspect it hits other platforms as well.

Revision history for this message
Peter Maydell (pmaydell) wrote :

I see this bug has been reported before, for instance here:
http://<email address hidden>/msg29250.html
and Laurent Desnogues noticed the problem while reviewing a patch in this area:
http://<email address hidden>/msg22251.html

The only code which looks at ts->info->host_argv is the code to handle SYS_GET_CMDLINE in the ARM semihosting support code arm-semi.c. My tentative suggestion is that we should instead make the semihosting support code read the argc/argv out of the userspace memory which loader_build_argptr has set up. (This is what linux-user/elfload.c does in fill_psinfo() as part of creating core dumps.)

Revision history for this message
Wolfgang Schildbach (wschi) wrote :
Download full text (4.5 KiB)

diff --git a/arm-semi.c b/arm-semi.c
index 0687b03..53b40e4 100644
--- a/arm-semi.c
+++ b/arm-semi.c
@@ -373,45 +373,48 @@ uint32_t do_arm_semihosting(CPUState *env)
 #ifdef CONFIG_USER_ONLY
         /* Build a commandline from the original argv. */
         {
- char **arg = ts->info->host_argv;
- int len = ARG(1);
- /* lock the buffer on the ARM side */
- char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0);
+ int i ;

- if (!cmdline_buffer)
- /* FIXME - should this error code be -TARGET_EFAULT ? */
- return (uint32_t)-1;
+ char *arm_cmdline_buffer ;
+ const char *host_cmdline_buffer ;

- s = cmdline_buffer;
- while (*arg && len > 2) {
- int n = strlen(*arg);
+ int arm_cmdline_len = ARG(1) ;
+ int host_cmdline_len = ts->info->arg_end-ts->info->arg_start ;

- if (s != cmdline_buffer) {
- *(s++) = ' ';
- len--;
- }
- if (n >= len)
- n = len - 1;
- memcpy(s, *arg, n);
- s += n;
- len -= n;
- arg++;
- }
- /* Null terminate the string. */
- *s = 0;
- len = s - cmdline_buffer;
+ if (host_cmdline_len > arm_cmdline_len)
+ return (uint32_t)-1; /* command line too long */
+
+ /* lock the buffers on the ARM side */
+ arm_cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0);
+ host_cmdline_buffer = (const char*)lock_user(PAGE_READ, ts->info->arg_start, host_cmdline_len, 0);

- /* Unlock the buffer on the ARM side. */
- unlock_user(cmdline_buffer, ARG(0), len);
+ if (arm_cmdline_buffer && host_cmdline_buffer)
+ {

- /* Adjust the commandline length argument. */
- SET_ARG(1, len);
+ /* the last argument is zero-terminated;
+ no need for additional termination */
+ memcpy(arm_cmdline_buffer, host_cmdline_buffer, host_cmdline_len);

- /* Return success if commandline fit into buffer. */
- return *arg ? -1 : 0;
+ /* separate arguments by white spaces */
+ for (i = 0; i < host_cmdline_len-1; i++) {
+ if (arm_cmdline_buffer[i] == 0) {
+ arm_cmdline_buffer[i] = ' ';
+ }
+ }
+
+ /* Adjust the commandline length argument. */
+ SET_ARG(1, host_cmdline_len);
+ }
+
+ /* Unlock the buffers on the ARM side. */
+ unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len);
+ unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, host_cmdline_len);
+
+ /* Return success if we could return a commandline. */
+ return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1;
         }
 #else
- return -1;
+ return -1;
 #endif
     case S...

Read more...

Revision history for this message
Wolfgang Schildbach (wschi) wrote :

Hi Peter,

Is this what you had in mind?

Aurelien Jarno (aurel32)
Changed in qemu:
status: New → Fix Committed
Aurelien Jarno (aurel32)
Changed in qemu:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.