diff mbox series

semihosting/uaccess.c: Replaced a malloc call with g_malloc.

Message ID 20230725090039.1271-1-dinglimin@cmss.chinamobile.com
State New
Headers show
Series semihosting/uaccess.c: Replaced a malloc call with g_malloc. | expand

Commit Message

dinglimin July 25, 2023, 9 a.m. UTC
Replaced a call to malloc() and its respective call to free() with g_malloc() and g_free().

Signed-off-by: dinglimin <dinglimin@cmss.chinamobile.com>

V1 -> V2:if cpu_memory_rw_debug failed, still need to set p=NULL
---
 semihosting/uaccess.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Comments

Michael Tokarev July 25, 2023, 9:13 a.m. UTC | #1
25.07.2023 12:00, dinglimin wrote:
> Replaced a call to malloc() and its respective call to free() with g_malloc() and g_free().
> 
> Signed-off-by: dinglimin <dinglimin@cmss.chinamobile.com>
> 
> V1 -> V2:if cpu_memory_rw_debug failed, still need to set p=NULL
> ---
>   semihosting/uaccess.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c
> index 8018828069..2ac754cdb6 100644
> --- a/semihosting/uaccess.c
> +++ b/semihosting/uaccess.c
> @@ -14,10 +14,10 @@
>   void *softmmu_lock_user(CPUArchState *env, target_ulong addr,
>                           target_ulong len, bool copy)
>   {
> -    void *p = malloc(len);
> +    void *p = g_malloc(len);
>       if (p && copy) {
>           if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) {
> -            free(p);
> +            g_free(p);
>               p = NULL;
>           }
>       }

Ok, that was the obvious part.  Now a next one, also obvious.

You changed lock_user to use g_malloc(), but unlock_user
still uses free() instead of g_free().  At the very least
the other one needs to be changed too.  And I'd say the callers
should be analyzed to ensure they don't free() the result
(they should not, think it is a bug if they do).

lock_user/unlock_user (which #defines to softmmu_lock_user/
softmmu_unlock_user in softmmu mode) is used a *lot*.

/mjt
Peter Maydell July 25, 2023, 9:35 a.m. UTC | #2
On Tue, 25 Jul 2023 at 10:13, Michael Tokarev <mjt@tls.msk.ru> wrote:
>
> 25.07.2023 12:00, dinglimin wrote:
> > Replaced a call to malloc() and its respective call to free() with g_malloc() and g_free().
> >
> > Signed-off-by: dinglimin <dinglimin@cmss.chinamobile.com>
> >
> > V1 -> V2:if cpu_memory_rw_debug failed, still need to set p=NULL
> > ---
> >   semihosting/uaccess.c | 4 ++--
> >   1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c
> > index 8018828069..2ac754cdb6 100644
> > --- a/semihosting/uaccess.c
> > +++ b/semihosting/uaccess.c
> > @@ -14,10 +14,10 @@
> >   void *softmmu_lock_user(CPUArchState *env, target_ulong addr,
> >                           target_ulong len, bool copy)
> >   {
> > -    void *p = malloc(len);
> > +    void *p = g_malloc(len);
> >       if (p && copy) {
> >           if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) {
> > -            free(p);
> > +            g_free(p);
> >               p = NULL;
> >           }
> >       }
>
> Ok, that was the obvious part.  Now a next one, also obvious.
>
> You changed lock_user to use g_malloc(), but unlock_user
> still uses free() instead of g_free().  At the very least
> the other one needs to be changed too.  And I'd say the callers
> should be analyzed to ensure they don't free() the result
> (they should not, think it is a bug if they do).

We can be pretty sure the callers don't free() the returned
value, because the calling code is also used in user-mode,
where the lock/unlock implementation is entirely different
and calling free() on the pointer will not work.

> lock_user/unlock_user (which #defines to softmmu_lock_user/
> softmmu_unlock_user in softmmu mode) is used a *lot*.

The third part here, is that g_malloc() does not ever
fail -- it will abort() on out of memory. However
the code here is still handling g_malloc() returning NULL.
The equivalent for "we expect this might fail" (which we want
here, because the guest is passing us the length of memory
to try to allocate) is g_try_malloc().

thanks
-- PMM
dinglimin July 26, 2023, 4:37 a.m. UTC | #3
>On Tue, 25 Jul 2023 at 10:13, Michael Tokarev <mjt@tls.msk.ru> wrote:
> >
> > 25.07.2023 12:00, dinglimin wrote:
> > > Replaced a call to malloc() and its respective call to free() with g_malloc() and g_free().
> > >
> > > Signed-off-by: dinglimin <dinglimin@cmss.chinamobile.com>
> > >
> > > V1 -> V2:if cpu_memory_rw_debug failed, still need to set p=NULL
> > > ---
> > >   semihosting/uaccess.c | 4 ++--
> > >   1 file changed, 2 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c
> > > index 8018828069..2ac754cdb6 100644
> > > --- a/semihosting/uaccess.c
> > > +++ b/semihosting/uaccess.c
> > > @@ -14,10 +14,10 @@
> > >   void *softmmu_lock_user(CPUArchState *env, target_ulong addr,
> > >                           target_ulong len, bool copy)
> > >   {
> > > -    void *p = malloc(len);
> > > +    void *p = g_malloc(len);
> > >       if (p && copy) {
> > >           if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) {
> > > -            free(p);
> > > +            g_free(p);
> > >               p = NULL;
> > >           }
>> >       }
> >
>> Ok, that was the obvious part.  Now a next one, also obvious.
> >
> > You changed lock_user to use g_malloc(), but unlock_user
> > still uses free() instead of g_free().  At the very least
> > the other one needs to be changed too.  And I'd say the callers
> > should be analyzed to ensure they don't free() the result
> (they should not, think it is a bug if they do).
>
> We can be pretty sure the callers don't free() the returned
> value, because the calling code is also used in user-mode,
> where the lock/unlock implementation is entirely different
> and calling free() on the pointer will not work.
> 
> > lock_user/unlock_user (which #defines to softmmu_lock_user/
> > softmmu_unlock_user in softmmu mode) is used a *lot*.
> 
> The third part here, is that g_malloc() does not ever
> fail -- it will abort() on out of memory. However
> the code here is still handling g_malloc() returning NULL.
> The equivalent for "we expect this might fail" (which we want
> here, because the guest is passing us the length of memory
> to try to allocate) is g_try_malloc().
> 
> thanks
> -- PMM
g_malloc() is preferred more than g_try_* functions, which return NULL on error,
 when the size of the requested allocation  is small. 
This is because allocating few bytes should not be a problem in a healthy system. 
Otherwise, the system is already in a critical state.

Plan to delete null checks after g malloc().


发件人: Peter Maydell
发送时间: 2023年7月25日 17:35
收件人: Michael Tokarev
抄送: dinglimin; richard.henderson@linaro.org; qemu-devel@nongnu.org
主题: Re: [PATCH] semihosting/uaccess.c: Replaced a malloc call with g_malloc.

On Tue, 25 Jul 2023 at 10:13, Michael Tokarev <mjt@tls.msk.ru> wrote:
>
> 25.07.2023 12:00, dinglimin wrote:
> > Replaced a call to malloc() and its respective call to free() with g_malloc() and g_free().
> >
> > Signed-off-by: dinglimin <dinglimin@cmss.chinamobile.com>
> >
> > V1 -> V2:if cpu_memory_rw_debug failed, still need to set p=NULL
> > ---
> >   semihosting/uaccess.c | 4 ++--
> >   1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c
> > index 8018828069..2ac754cdb6 100644
> > --- a/semihosting/uaccess.c
> > +++ b/semihosting/uaccess.c
> > @@ -14,10 +14,10 @@
> >   void *softmmu_lock_user(CPUArchState *env, target_ulong addr,
> >                           target_ulong len, bool copy)
> >   {
> > -    void *p = malloc(len);
> > +    void *p = g_malloc(len);
> >       if (p && copy) {
> >           if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) {
> > -            free(p);
> > +            g_free(p);
> >               p = NULL;
> >           }
> >       }
>
> Ok, that was the obvious part.  Now a next one, also obvious.
>
> You changed lock_user to use g_malloc(), but unlock_user
> still uses free() instead of g_free().  At the very least
> the other one needs to be changed too.  And I'd say the callers
> should be analyzed to ensure they don't free() the result
> (they should not, think it is a bug if they do).

We can be pretty sure the callers don't free() the returned
value, because the calling code is also used in user-mode,
where the lock/unlock implementation is entirely different
and calling free() on the pointer will not work.

> lock_user/unlock_user (which #defines to softmmu_lock_user/
> softmmu_unlock_user in softmmu mode) is used a *lot*.

The third part here, is that g_malloc() does not ever
fail -- it will abort() on out of memory. However
the code here is still handling g_malloc() returning NULL.
The equivalent for "we expect this might fail" (which we want
here, because the guest is passing us the length of memory
to try to allocate) is g_try_malloc().

thanks
-- PMM
Peter Maydell July 26, 2023, 9:43 a.m. UTC | #4
(Something went wrong with the quoting in your email. I've
fixed it up.)

On Wed, 26 Jul 2023 at 05:38, <dinglimin@cmss.chinamobile.com> wrote:
> Peter Maydell wrote:
> > The third part here, is that g_malloc() does not ever
> > fail -- it will abort() on out of memory. However
> > the code here is still handling g_malloc() returning NULL.
> > The equivalent for "we expect this might fail" (which we want
> > here, because the guest is passing us the length of memory
> > to try to allocate) is g_try_malloc().

> g_malloc() is preferred more than g_try_* functions, which return NULL on error,
> when the size of the requested allocation  is small.
> This is because allocating few bytes should not be a problem in a healthy system.

This is true. But in this particular case we cannot be sure
that the size of the allocation is small, because the size
is controlled by the guest. So we want g_try_malloc().

thanks
-- PMM
Richard Henderson July 26, 2023, 3:21 p.m. UTC | #5
On 7/26/23 02:43, Peter Maydell wrote:
> (Something went wrong with the quoting in your email. I've
> fixed it up.)
> 
> On Wed, 26 Jul 2023 at 05:38, <dinglimin@cmss.chinamobile.com> wrote:
>> Peter Maydell wrote:
>>> The third part here, is that g_malloc() does not ever
>>> fail -- it will abort() on out of memory. However
>>> the code here is still handling g_malloc() returning NULL.
>>> The equivalent for "we expect this might fail" (which we want
>>> here, because the guest is passing us the length of memory
>>> to try to allocate) is g_try_malloc().
> 
>> g_malloc() is preferred more than g_try_* functions, which return NULL on error,
>> when the size of the requested allocation  is small.
>> This is because allocating few bytes should not be a problem in a healthy system.
> 
> This is true. But in this particular case we cannot be sure
> that the size of the allocation is small, because the size
> is controlled by the guest. So we want g_try_malloc().

And why do we want to use g_try_malloc instead of just sticking with malloc?

I see no reason to change anything at all here.


r~
Peter Maydell July 27, 2023, 2:56 p.m. UTC | #6
On Wed, 26 Jul 2023 at 16:21, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 7/26/23 02:43, Peter Maydell wrote:
> > (Something went wrong with the quoting in your email. I've
> > fixed it up.)
> >
> > On Wed, 26 Jul 2023 at 05:38, <dinglimin@cmss.chinamobile.com> wrote:
> >> Peter Maydell wrote:
> >>> The third part here, is that g_malloc() does not ever
> >>> fail -- it will abort() on out of memory. However
> >>> the code here is still handling g_malloc() returning NULL.
> >>> The equivalent for "we expect this might fail" (which we want
> >>> here, because the guest is passing us the length of memory
> >>> to try to allocate) is g_try_malloc().
> >
> >> g_malloc() is preferred more than g_try_* functions, which return NULL on error,
> >> when the size of the requested allocation  is small.
> >> This is because allocating few bytes should not be a problem in a healthy system.
> >
> > This is true. But in this particular case we cannot be sure
> > that the size of the allocation is small, because the size
> > is controlled by the guest. So we want g_try_malloc().
>
> And why do we want to use g_try_malloc instead of just sticking with malloc?

The only real reason is just consistency -- the project uses
the glib malloc wrappers, and in theory any use of raw
malloc() ought to be either:
 * something that's third party library code (eg libdecnumber)
 * because it's going into a standalone program that doesn't
   link against glib
 * for a special case reason which is documented in a
   nearby comment (eg because the memory is going to be
   passed to some library which documents that it will assume
   it can free() the memory)

We're down to very few uses of malloc() that don't fall
into one of those cases:

bsd-user/elfload.c
a few uses in disas/ (m68k, sparc, nios2)
hw/audio/fmopl.c
semihosting/uaccess.c
target/xtensa/translate.c
contrib/elf2dmp/
(and maybe tests/qtest/fuzz/qtest_wrappers.c : I'm not
sure what environment that gets built in.)

The main reason we get patches like this is that the
"bite sized tasks" list at
https://wiki.qemu.org/Contribute/BiteSizedTasks
has an entry for "convert malloc uses to g_new or similar".
The trouble with that is that almost all of the low-hanging
fruit was converted a long time ago, so the remainder
are getting increasingly tricky to analyse and increasingly
less worthwhile...

thanks
-- PMM
Daniel P. Berrangé July 27, 2023, 3:04 p.m. UTC | #7
On Thu, Jul 27, 2023 at 03:56:23PM +0100, Peter Maydell wrote:
> On Wed, 26 Jul 2023 at 16:21, Richard Henderson
> <richard.henderson@linaro.org> wrote:
> >
> > On 7/26/23 02:43, Peter Maydell wrote:
> > > (Something went wrong with the quoting in your email. I've
> > > fixed it up.)
> > >
> > > On Wed, 26 Jul 2023 at 05:38, <dinglimin@cmss.chinamobile.com> wrote:
> > >> Peter Maydell wrote:
> > >>> The third part here, is that g_malloc() does not ever
> > >>> fail -- it will abort() on out of memory. However
> > >>> the code here is still handling g_malloc() returning NULL.
> > >>> The equivalent for "we expect this might fail" (which we want
> > >>> here, because the guest is passing us the length of memory
> > >>> to try to allocate) is g_try_malloc().
> > >
> > >> g_malloc() is preferred more than g_try_* functions, which return NULL on error,
> > >> when the size of the requested allocation  is small.
> > >> This is because allocating few bytes should not be a problem in a healthy system.
> > >
> > > This is true. But in this particular case we cannot be sure
> > > that the size of the allocation is small, because the size
> > > is controlled by the guest. So we want g_try_malloc().
> >
> > And why do we want to use g_try_malloc instead of just sticking with malloc?
> 
> The only real reason is just consistency

I think it is slightly stronger than that.

By using g_try_malloc we make it explicit that this scenario is
expecting the allocation to fail and needs to handle that.

If we use plain 'malloc' it isn't clear whether we genuinely expect
the allocation to fail, or someone just blindly checked malloc
return value out of habit, because they didn't realize QEMU wants
abort-on-OOM behaviour most of the time.

With regards,
Daniel
Richard Henderson July 27, 2023, 4:31 p.m. UTC | #8
On 7/27/23 08:04, Daniel P. Berrangé wrote:
> On Thu, Jul 27, 2023 at 03:56:23PM +0100, Peter Maydell wrote:
>> On Wed, 26 Jul 2023 at 16:21, Richard Henderson
>> <richard.henderson@linaro.org> wrote:
>>>
>>> On 7/26/23 02:43, Peter Maydell wrote:
>>>> (Something went wrong with the quoting in your email. I've
>>>> fixed it up.)
>>>>
>>>> On Wed, 26 Jul 2023 at 05:38, <dinglimin@cmss.chinamobile.com> wrote:
>>>>> Peter Maydell wrote:
>>>>>> The third part here, is that g_malloc() does not ever
>>>>>> fail -- it will abort() on out of memory. However
>>>>>> the code here is still handling g_malloc() returning NULL.
>>>>>> The equivalent for "we expect this might fail" (which we want
>>>>>> here, because the guest is passing us the length of memory
>>>>>> to try to allocate) is g_try_malloc().
>>>>
>>>>> g_malloc() is preferred more than g_try_* functions, which return NULL on error,
>>>>> when the size of the requested allocation  is small.
>>>>> This is because allocating few bytes should not be a problem in a healthy system.
>>>>
>>>> This is true. But in this particular case we cannot be sure
>>>> that the size of the allocation is small, because the size
>>>> is controlled by the guest. So we want g_try_malloc().
>>>
>>> And why do we want to use g_try_malloc instead of just sticking with malloc?
>>
>> The only real reason is just consistency
> 
> I think it is slightly stronger than that.
> 
> By using g_try_malloc we make it explicit that this scenario is
> expecting the allocation to fail and needs to handle that.
> 
> If we use plain 'malloc' it isn't clear whether we genuinely expect
> the allocation to fail, or someone just blindly checked malloc
> return value out of habit, because they didn't realize QEMU wants
> abort-on-OOM behaviour most of the time.

Ok, fair enough.


r~
Peter Maydell July 28, 2023, 12:16 p.m. UTC | #9
On Thu, 27 Jul 2023 at 15:56, Peter Maydell <peter.maydell@linaro.org> wrote:
> The only real reason is just consistency -- the project uses
> the glib malloc wrappers, and in theory any use of raw
> malloc() ought to be either:
>  * something that's third party library code (eg libdecnumber)
>  * because it's going into a standalone program that doesn't
>    link against glib
>  * for a special case reason which is documented in a
>    nearby comment (eg because the memory is going to be
>    passed to some library which documents that it will assume
>    it can free() the memory)

I wrote up a gitlab bite-sized-task issue for the remaining
conversions which goes into a bit more detail about some
of the pitfalls to watch out for, and made the bitesizedtasks
wiki page link to that:
 https://gitlab.com/qemu-project/qemu/-/issues/1798

thanks
-- PMM
diff mbox series

Patch

diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c
index 8018828069..2ac754cdb6 100644
--- a/semihosting/uaccess.c
+++ b/semihosting/uaccess.c
@@ -14,10 +14,10 @@ 
 void *softmmu_lock_user(CPUArchState *env, target_ulong addr,
                         target_ulong len, bool copy)
 {
-    void *p = malloc(len);
+    void *p = g_malloc(len);
     if (p && copy) {
         if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) {
-            free(p);
+            g_free(p);
             p = NULL;
         }
     }