[04/16] libelf: Fix deadlock in elf_cntl

Message ID 20231010134300.53830-4-mark@klomp.org
State Changes Requested
Headers
Series [01/16] lib: Add new once_define and once macros to eu-config.h |

Commit Message

Mark Wielaard Oct. 10, 2023, 1:42 p.m. UTC
  From: Heather McIntyre <hsm2@rice.edu>

	* libelf/elf_cntl.c (elf_cntl): Move rwlock_wrlock, rwlock_unlock,
	inside case switch statements.

Signed-off-by: Heather S. McIntyre <hsm2@rice.edu>
Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 libelf/elf_cntl.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
  

Comments

Mark Wielaard Oct. 10, 2023, 3:23 p.m. UTC | #1
Hi Heather,

On Tue, 2023-10-10 at 15:42 +0200, Mark Wielaard wrote:
> From: Heather McIntyre <hsm2@rice.edu>
> 
> 	* libelf/elf_cntl.c (elf_cntl): Move rwlock_wrlock, rwlock_unlock,
> 	inside case switch statements.
> 
> Signed-off-by: Heather S. McIntyre <hsm2@rice.edu>
> Signed-off-by: Mark Wielaard <mark@klomp.org>
> ---
>  libelf/elf_cntl.c | 11 +++++++----
>  1 file changed, 7 insertions(+), 4 deletions(-)
> 
> diff --git a/libelf/elf_cntl.c b/libelf/elf_cntl.c
> index 04aa9132..64087c7d 100644
> --- a/libelf/elf_cntl.c
> +++ b/libelf/elf_cntl.c
> @@ -48,13 +48,16 @@ elf_cntl (Elf *elf, Elf_Cmd cmd)
>        return -1;
>      }
>  
> -  rwlock_wrlock (elf->lock);
> +
>  
>    switch (cmd)
>      {
>      case ELF_C_FDREAD:
> +     rwlock_rdlock (elf->lock);
> +     int addr_isnull = elf->map_address == NULL;
> +     rwlock_unlock(elf->lock);
>        /* If not all of the file is in the memory read it now.  */
> -      if (elf->map_address == NULL && __libelf_readall (elf) == NULL)
> +      if (addr_isnull && __libelf_readall (elf) == NULL)
>  	{
>  	  /* We were not able to read everything.  */
>  	  result = -1;

Can't we just rely on if (__libelf_readall (elf) == NULL)?

__libelf_readall already does locking and will return non-NULL if elf-
>map_address is already set. So it looks like the extra check (and
locking) to check addr_isnull is redundant and just make the code more
complex.

> @@ -64,7 +67,9 @@ elf_cntl (Elf *elf, Elf_Cmd cmd)
>  
>      case ELF_C_FDDONE:
>        /* Mark the file descriptor as not usable.  */
> +      rwlock_wrlock (elf->lock);
>        elf->fildes = -1;
> +      rwlock_unlock (elf->lock);
>        break;
>  
>      default:

This looks correct. All other accesses to elf->fildes seem to be done
under the elf->lock too.

> @@ -73,7 +78,5 @@ elf_cntl (Elf *elf, Elf_Cmd cmd)
>        break;
>      }
>  
> -  rwlock_unlock (elf->lock);
> -
>    return result;
>  }
  
Heather McIntyre Oct. 17, 2023, 7:14 p.m. UTC | #2
You are right. I changed the code to just rely on if (__libelf_readall
(elf) == NULL) and this seems to work just fine.

On Tue, Oct 10, 2023 at 10:23 AM Mark Wielaard <mark@klomp.org> wrote:

> Hi Heather,
>
> On Tue, 2023-10-10 at 15:42 +0200, Mark Wielaard wrote:
> > From: Heather McIntyre <hsm2@rice.edu>
> >
> >       * libelf/elf_cntl.c (elf_cntl): Move rwlock_wrlock, rwlock_unlock,
> >       inside case switch statements.
> >
> > Signed-off-by: Heather S. McIntyre <hsm2@rice.edu>
> > Signed-off-by: Mark Wielaard <mark@klomp.org>
> > ---
> >  libelf/elf_cntl.c | 11 +++++++----
> >  1 file changed, 7 insertions(+), 4 deletions(-)
> >
> > diff --git a/libelf/elf_cntl.c b/libelf/elf_cntl.c
> > index 04aa9132..64087c7d 100644
> > --- a/libelf/elf_cntl.c
> > +++ b/libelf/elf_cntl.c
> > @@ -48,13 +48,16 @@ elf_cntl (Elf *elf, Elf_Cmd cmd)
> >        return -1;
> >      }
> >
> > -  rwlock_wrlock (elf->lock);
> > +
> >
> >    switch (cmd)
> >      {
> >      case ELF_C_FDREAD:
> > +     rwlock_rdlock (elf->lock);
> > +     int addr_isnull = elf->map_address == NULL;
> > +     rwlock_unlock(elf->lock);
> >        /* If not all of the file is in the memory read it now.  */
> > -      if (elf->map_address == NULL && __libelf_readall (elf) == NULL)
> > +      if (addr_isnull && __libelf_readall (elf) == NULL)
> >       {
> >         /* We were not able to read everything.  */
> >         result = -1;
>
> Can't we just rely on if (__libelf_readall (elf) == NULL)?
>
> __libelf_readall already does locking and will return non-NULL if elf-
> >map_address is already set. So it looks like the extra check (and
> locking) to check addr_isnull is redundant and just make the code more
> complex.
>
> > @@ -64,7 +67,9 @@ elf_cntl (Elf *elf, Elf_Cmd cmd)
> >
> >      case ELF_C_FDDONE:
> >        /* Mark the file descriptor as not usable.  */
> > +      rwlock_wrlock (elf->lock);
> >        elf->fildes = -1;
> > +      rwlock_unlock (elf->lock);
> >        break;
> >
> >      default:
>
> This looks correct. All other accesses to elf->fildes seem to be done
> under the elf->lock too.
>
> > @@ -73,7 +78,5 @@ elf_cntl (Elf *elf, Elf_Cmd cmd)
> >        break;
> >      }
> >
> > -  rwlock_unlock (elf->lock);
> > -
> >    return result;
> >  }
>
>
  

Patch

diff --git a/libelf/elf_cntl.c b/libelf/elf_cntl.c
index 04aa9132..64087c7d 100644
--- a/libelf/elf_cntl.c
+++ b/libelf/elf_cntl.c
@@ -48,13 +48,16 @@  elf_cntl (Elf *elf, Elf_Cmd cmd)
       return -1;
     }
 
-  rwlock_wrlock (elf->lock);
+
 
   switch (cmd)
     {
     case ELF_C_FDREAD:
+     rwlock_rdlock (elf->lock);
+     int addr_isnull = elf->map_address == NULL;
+     rwlock_unlock(elf->lock);
       /* If not all of the file is in the memory read it now.  */
-      if (elf->map_address == NULL && __libelf_readall (elf) == NULL)
+      if (addr_isnull && __libelf_readall (elf) == NULL)
 	{
 	  /* We were not able to read everything.  */
 	  result = -1;
@@ -64,7 +67,9 @@  elf_cntl (Elf *elf, Elf_Cmd cmd)
 
     case ELF_C_FDDONE:
       /* Mark the file descriptor as not usable.  */
+      rwlock_wrlock (elf->lock);
       elf->fildes = -1;
+      rwlock_unlock (elf->lock);
       break;
 
     default:
@@ -73,7 +78,5 @@  elf_cntl (Elf *elf, Elf_Cmd cmd)
       break;
     }
 
-  rwlock_unlock (elf->lock);
-
   return result;
 }