Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak when assigning to %ENV #10242

Closed
p5pRT opened this issue Mar 20, 2010 · 7 comments
Closed

Memory leak when assigning to %ENV #10242

p5pRT opened this issue Mar 20, 2010 · 7 comments

Comments

@p5pRT
Copy link

p5pRT commented Mar 20, 2010

Migrated from rt.perl.org#73672 (status was 'rejected')

Searchable as RT73672$

@p5pRT
Copy link
Author

p5pRT commented Mar 20, 2010

From andrew.speer@isolutions.com.au

Created by andrew.speer@isolutions.com.au

Perl 5.10.0 on Fedora 12 leaks memory when assigning to %ENV.

The following command line will reproduce the problem.

perl -e '$ENV{foo}=1 while 1'

Perl Info

Flags:
    category=core
    severity=medium

This perlbug was built using Perl 5.10.0 in the Fedora build system.
It is being executed now by Perl 5.10.0 - Tue Dec  1 10:20:28 EST 2009.

Site configuration information for perl 5.10.0:

Configured by Red Hat, Inc. at Tue Dec  1 10:20:28 EST 2009.

Summary of my perl5 (revision 5 version 10 subversion 0) configuration:
  Platform:
    osname=linux, osvers=2.6.18-164.2.1.el5, archname=i386-linux-thread-multi
    uname='linux x86-3.fedora.phx.redhat.com 2.6.18-164.2.1.el5 #1 smp mon sep 21 04:37:42 edt 2009 i686 i686 i386 gnulinux '
    config_args='-des -Doptimize=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables -Accflags=-DPERL_USE_SAFE_PUTENV -Dversion=5.10.0 -Dmyhostname=localhost -Dperladmin=root@localhost -Dcc=gcc -Dcf_by=Red Hat, Inc. -Dprefix=/usr -Dvendorprefix=/usr -Dsiteprefix=/usr/local -Dprivlib=/usr/lib/perl5/5.10.0 -Dsitelib=/usr/local/lib/perl5/site_perl/5.10.0 -Dvendorlib=/usr/lib/perl5/vendor_perl/5.10.0 -Darchlib=/usr/lib/perl5/5.10.0/i386-linux-thread-multi -Dsitearch=/usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi -Dvendorarch=/usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi -Dinc_version_list=none -Darchname=i386-linux-thread-multi -Duseshrplib -Dusethreads -Duseithreads -Duselargefiles -Dd_dosuid -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Duseperlio -Dinstallusrbinperl=n -Ubincompat5005 -Uversiononly -Dpager=/usr/bi!
 n/less -isr -Dd_gethostent_r_proto -Ud_endhostent_r_proto -Ud_sethostent_r_proto -Ud_endprotoent_r_proto -Ud_setprotoent_r_proto -Ud_endservent_r_proto -Ud_setservent_r_proto -Dscriptdir=/usr/bin -Dotherlibdirs=/usr/lib/perl5/site_perl'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=define, usemultiplicity=define
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
    use64bitint=undef, use64bitall=undef, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DPERL_USE_SAFE_PUTENV -DDEBUGGING -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
    optimize='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables',
    cppflags='-D_REENTRANT -D_GNU_SOURCE -DPERL_USE_SAFE_PUTENV -DDEBUGGING -fno-strict-aliasing -pipe -I/usr/local/include -I/usr/include/gdbm'
    ccversion='', gccversion='4.4.2 20091027 (Red Hat 4.4.2-7)', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=4, prototype=define
  Linker and Libraries:
    ld='gcc', ldflags =' -L/usr/local/lib'
    libpth=/usr/local/lib /lib /usr/lib
    libs=-lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc
    perllibs=-lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc
    libc=/lib/libc-2.11.so, so=so, useshrplib=true, libperl=libperl.so
    gnulibc_version='2.11'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E -Wl,-rpath,/usr/lib/perl5/5.10.0/i386-linux-thread-multi/CORE'
    cccdlflags='-fPIC', lddlflags='-shared -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables -L/usr/local/lib'

Locally applied patches:
    


@INC for perl 5.10.0:
    /opt/perl5lib.local/i386-linux-thread-multi
    /opt/perl5lib.local
    /opt/perl5lib
    /usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi
    /usr/local/lib/perl5/site_perl/5.10.0
    /usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi
    /usr/lib/perl5/vendor_perl/5.10.0
    /usr/lib/perl5/vendor_perl
    /usr/lib/perl5/5.10.0/i386-linux-thread-multi
    /usr/lib/perl5/5.10.0
    /usr/lib/perl5/site_perl
    .


Environment for perl 5.10.0:
    HOME=/root
    LANG=en_US.UTF-8
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/usr/lib/qt-3.3/bin:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
    PERL5LIB=/opt/perl5lib.local:/opt/perl5lib
    PERL_BADLANG (unset)
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Mar 21, 2010

From @iabyn

On Fri, Mar 19, 2010 at 11​:31​:39PM -0700, andrew.speer@​isolutions.com.au wrote​:

Perl 5.10.0 on Fedora 12 leaks memory when assigning to %ENV.

The following command line will reproduce the problem.

perl -e '$ENV{foo}=1 while 1'

[snip]

The fedora Perl is built with the following​:

config\_args= \.\.\. \-Accflags=\-DPERL\_USE\_SAFE\_PUTENV \.\.\.

and from INSTALL​:

=head3 Environment access

Perl often needs to write to the program's environment, such as when C<%ENV>
is assigned to. Many implementations of the C library function C<putenv()>
leak memory, so where possible perl will manipulate the environment directly
to avoid these leaks. The default is now to perform direct manipulation
whenever perl is running as a stand alone interpreter, and to call the safe
but potentially leaky C<putenv()> function when the perl interpreter is
embedded in another application. You can force perl to always use C<putenv()>
by compiling with -DPERL_USE_SAFE_PUTENV. You can force an embedded perl to
use direct manipulation by setting C<PL_use_safe_putenv = 0;> after the
C<perl_construct()> call.

Basically Fedora have chosen to build their perl using the OS's leaky
interface to the environment. So not really a perl bug.

For the curious, the problem with the putenv() library function on many
OSes is that it keeps pointers to the string you pass to it, so you can't
free it. The C equivalent of the leaky perl code above is

  while (1) {
  char *s = malloc(4);
  s[0] = 'A';
  s[1] = '=';
  s[2] = 'b';
  s[3] = '\0';

  putenv(s);
  /* not allowed​: free(s) */
  }

--
My get-up-and-go just got up and went.

@p5pRT
Copy link
Author

p5pRT commented Mar 21, 2010

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Mar 21, 2010

@iabyn - Status changed from 'open' to 'rejected'

@p5pRT p5pRT closed this as completed Mar 21, 2010
@p5pRT
Copy link
Author

p5pRT commented Mar 22, 2010

From andrew.speer@isolutions.com.au

On Sun Mar 21 05​:33​:06 2010, davem wrote​:
[snip]

The fedora Perl is built with the following​:

config\_args= \.\.\. \-Accflags=\-DPERL\_USE\_SAFE\_PUTENV \.\.\.

and from INSTALL​:

Thanks,

I'll report to Fedora Bugzilla and see if anyone is motivated to change it.

It causes issues for me because the FCGI (FastCGI) module uses %ENV as a
default parameter in FCGI​::Request. When using a FastCGI application
%ENV is updated continually as each web request comes in - in this case
leaking memory and eventually killing the FastCGI process.

Thanks again for your quick response.

Regards,

Andrew Speer

@p5pRT
Copy link
Author

p5pRT commented Mar 22, 2010

From [Unknown Contact. See original ticket]

On Sun Mar 21 05​:33​:06 2010, davem wrote​:
[snip]

The fedora Perl is built with the following​:

config\_args= \.\.\. \-Accflags=\-DPERL\_USE\_SAFE\_PUTENV \.\.\.

and from INSTALL​:

Thanks,

I'll report to Fedora Bugzilla and see if anyone is motivated to change it.

It causes issues for me because the FCGI (FastCGI) module uses %ENV as a
default parameter in FCGI​::Request. When using a FastCGI application
%ENV is updated continually as each web request comes in - in this case
leaking memory and eventually killing the FastCGI process.

Thanks again for your quick response.

Regards,

Andrew Speer

@p5pRT
Copy link
Author

p5pRT commented Jul 26, 2010

From @ppisar

Dne ne 21.bře.2010 05​:33​:06, davem napsal(a)​:

For the curious, the problem with the putenv() library function on
many
OSes is that it keeps pointers to the string you pass to it, so you
can't
free it.

For curious, the putenv(3) has never been intended to provide such
functionality. If application wants to replace variable put by putenv(),
it will need to save pointer before putting to keep a reference to pass
it to subsequent free().

The right functions to manipulate environment is setenv(3) and
unsetenv(3) standardized in POSIX.1-2001. More ever POSIX does not
define a way how operating system should store environment variables.
It's implementation specific and application cannot assume anything
about it (like perl exhibits without the macro in question).

Funny thing is perl's util.c code offers the {un,}seteenv() approach,
however only for obscure platforms like Cygwin or Symbian​:

# if defined(__CYGWIN__) || defined(EPOC) || defined(__SYMBIAN32__) ||
defined(__riscos__)
# if defined(HAS_UNSETENV)
  if (val == NULL) {
  (void)unsetenv(nam);
  } else {
  (void)setenv(nam, val, 1);
  }
# else /* ! HAS_UNSETENV */
  (void)setenv(nam, val, 1);
# endif /* HAS_UNSETENV */
# else
# if defined(HAS_UNSETENV)
  if (val == NULL) {
  (void)unsetenv(nam);
  } else {
  const int nlen = strlen(nam);
  const int vlen = strlen(val);
  char * const new_env =
  (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
  my_setenv_format(new_env, nam, nlen, val, vlen);
  (void)putenv(new_env);
  }
# else /* ! HAS_UNSETENV */
  char *new_env;
  const int nlen = strlen(nam);
  int vlen;
  if (!val) {
  val = "";
  }
  vlen = strlen(val);
  new_env = (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
  /* all that work just for this */
  my_setenv_format(new_env, nam, nlen, val, vlen);
  (void)putenv(new_env);
# endif /* HAS_UNSETENV */
# endif /* __CYGWIN__ */

According my opinion, the ifdefed branch should be made default one and
the else branch an exception for broken platforms. E.g. glibc's setenv()
is leak-proof.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant