nscd: Use struct scratch_buffer, not extend_alloca in most caches [BZ #18023]

This replaces the ERANGE retry loops with loops which have heap
fallback.  Heap allocation might actually be required for extremely
large NSS results.
This commit is contained in:
Florian Weimer 2018-06-25 16:04:29 +02:00
parent 318bad78b0
commit 2f9f0d182e
5 changed files with 84 additions and 152 deletions

View file

@ -1,3 +1,12 @@
2018-06-25 Florian Weimer <fweimer@redhat.com>
[BZ #18023]
* nscd/grpcache.c (addgrbyX): Use struct scratch_buffer instead
of extend_alloca.
* nscd/hstcache.c (addhstbyX): Likewise.
* nscd/pwdcache.c (addpwbyX): Likewise.
* nscd/servicescache.c (addservbyX): Likewise.
2018-06-25 Florian Weimer <fweimer@redhat.com> 2018-06-25 Florian Weimer <fweimer@redhat.com>
[BZ #18023] [BZ #18023]

View file

@ -16,7 +16,6 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>. */ along with this program; if not, see <http://www.gnu.org/licenses/>. */
#include <alloca.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <error.h> #include <error.h>
@ -32,6 +31,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <stackinfo.h> #include <stackinfo.h>
#include <scratch_buffer.h>
#include "nscd.h" #include "nscd.h"
#include "dbg_log.h" #include "dbg_log.h"
@ -417,12 +417,12 @@ addgrbyX (struct database_dyn *db, int fd, request_header *req,
look again in the table whether the dataset is now available. We look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */ pruning function only will look at the timestamp. */
size_t buflen = 1024;
char *buffer = (char *) alloca (buflen);
struct group resultbuf; struct group resultbuf;
struct group *grp; struct group *grp;
bool use_malloc = false;
int errval = 0; int errval = 0;
struct scratch_buffer tmpbuf;
scratch_buffer_init (&tmpbuf);
if (__glibc_unlikely (debug_level > 0)) if (__glibc_unlikely (debug_level > 0))
{ {
@ -432,43 +432,24 @@ addgrbyX (struct database_dyn *db, int fd, request_header *req,
dbg_log (_("Reloading \"%s\" in group cache!"), keystr); dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
} }
while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0 while (lookup (req->type, key, &resultbuf,
tmpbuf.data, tmpbuf.length, &grp) != 0
&& (errval = errno) == ERANGE) && (errval = errno) == ERANGE)
{ if (!scratch_buffer_grow (&tmpbuf))
errno = 0; {
/* We ran out of memory. We cannot do anything but sending a
if (__glibc_unlikely (buflen > 32768)) negative response. In reality this should never
{ happen. */
char *old_buffer = buffer; grp = NULL;
buflen *= 2; /* We set the error to indicate this is (possibly) a temporary
buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); error and that it does not mean the entry is not available
if (buffer == NULL) at all. */
{ errval = EAGAIN;
/* We ran out of memory. We cannot do anything but break;
sending a negative response. In reality this should }
never happen. */
grp = NULL;
buffer = old_buffer;
/* We set the error to indicate this is (possibly) a
temporary error and that it does not mean the entry
is not available at all. */
errval = EAGAIN;
break;
}
use_malloc = true;
}
else
/* Allocate a new buffer on the stack. If possible combine it
with the previously allocated buffer. */
buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
}
time_t timeout = cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval); time_t timeout = cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
scratch_buffer_free (&tmpbuf);
if (use_malloc)
free (buffer);
return timeout; return timeout;
} }

View file

@ -34,6 +34,7 @@
#include <arpa/nameser.h> #include <arpa/nameser.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <stackinfo.h> #include <stackinfo.h>
#include <scratch_buffer.h>
#include "nscd.h" #include "nscd.h"
#include "dbg_log.h" #include "dbg_log.h"
@ -432,11 +433,8 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
look again in the table whether the dataset is now available. We look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */ pruning function only will look at the timestamp. */
int buflen = 1024;
char *buffer = (char *) alloca (buflen);
struct hostent resultbuf; struct hostent resultbuf;
struct hostent *hst; struct hostent *hst;
bool use_malloc = false;
int errval = 0; int errval = 0;
int32_t ttl = INT32_MAX; int32_t ttl = INT32_MAX;
@ -456,46 +454,30 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str); dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
} }
while (lookup (req->type, key, &resultbuf, buffer, buflen, &hst, &ttl) != 0 struct scratch_buffer tmpbuf;
scratch_buffer_init (&tmpbuf);
while (lookup (req->type, key, &resultbuf,
tmpbuf.data, tmpbuf.length, &hst, &ttl) != 0
&& h_errno == NETDB_INTERNAL && h_errno == NETDB_INTERNAL
&& (errval = errno) == ERANGE) && (errval = errno) == ERANGE)
{ if (!scratch_buffer_grow (&tmpbuf))
errno = 0; {
/* We ran out of memory. We cannot do anything but sending a
if (__glibc_unlikely (buflen > 32768)) negative response. In reality this should never
{ happen. */
char *old_buffer = buffer; hst = NULL;
buflen *= 2; /* We set the error to indicate this is (possibly) a temporary
buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); error and that it does not mean the entry is not
if (buffer == NULL) available at all. */
{ h_errno = TRY_AGAIN;
/* We ran out of memory. We cannot do anything but errval = EAGAIN;
sending a negative response. In reality this should break;
never happen. */ }
hst = NULL;
buffer = old_buffer;
/* We set the error to indicate this is (possibly) a
temporary error and that it does not mean the entry
is not available at all. */
h_errno = TRY_AGAIN;
errval = EAGAIN;
break;
}
use_malloc = true;
}
else
/* Allocate a new buffer on the stack. If possible combine it
with the previously allocated buffer. */
buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
}
time_t timeout = cache_addhst (db, fd, req, key, hst, uid, he, dh, time_t timeout = cache_addhst (db, fd, req, key, hst, uid, he, dh,
h_errno == TRY_AGAIN ? errval : 0, ttl); h_errno == TRY_AGAIN ? errval : 0, ttl);
scratch_buffer_free (&tmpbuf);
if (use_malloc)
free (buffer);
return timeout; return timeout;
} }

View file

@ -16,7 +16,6 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>. */ along with this program; if not, see <http://www.gnu.org/licenses/>. */
#include <alloca.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <error.h> #include <error.h>
@ -32,6 +31,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <stackinfo.h> #include <stackinfo.h>
#include <scratch_buffer.h>
#include "nscd.h" #include "nscd.h"
#include "dbg_log.h" #include "dbg_log.h"
@ -395,12 +395,11 @@ addpwbyX (struct database_dyn *db, int fd, request_header *req,
look again in the table whether the dataset is now available. We look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */ pruning function only will look at the timestamp. */
size_t buflen = 1024;
char *buffer = (char *) alloca (buflen);
struct passwd resultbuf; struct passwd resultbuf;
struct passwd *pwd; struct passwd *pwd;
bool use_malloc = false;
int errval = 0; int errval = 0;
struct scratch_buffer tmpbuf;
scratch_buffer_init (&tmpbuf);
if (__glibc_unlikely (debug_level > 0)) if (__glibc_unlikely (debug_level > 0))
{ {
@ -410,45 +409,26 @@ addpwbyX (struct database_dyn *db, int fd, request_header *req,
dbg_log (_("Reloading \"%s\" in password cache!"), keystr); dbg_log (_("Reloading \"%s\" in password cache!"), keystr);
} }
while (lookup (req->type, key, &resultbuf, buffer, buflen, &pwd) != 0 while (lookup (req->type, key, &resultbuf,
tmpbuf.data, tmpbuf.length, &pwd) != 0
&& (errval = errno) == ERANGE) && (errval = errno) == ERANGE)
{ if (!scratch_buffer_grow (&tmpbuf))
errno = 0; {
/* We ran out of memory. We cannot do anything but sending a
if (__glibc_unlikely (buflen > 32768)) negative response. In reality this should never
{ happen. */
char *old_buffer = buffer; pwd = NULL;
buflen *= 2; /* We set the error to indicate this is (possibly) a temporary
buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); error and that it does not mean the entry is not available
if (buffer == NULL) at all. */
{ errval = EAGAIN;
/* We ran out of memory. We cannot do anything but break;
sending a negative response. In reality this should }
never happen. */
pwd = NULL;
buffer = old_buffer;
/* We set the error to indicate this is (possibly) a
temporary error and that it does not mean the entry
is not available at all. */
errval = EAGAIN;
break;
}
use_malloc = true;
}
else
/* Allocate a new buffer on the stack. If possible combine it
with the previously allocated buffer. */
buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
}
/* Add the entry to the cache. */ /* Add the entry to the cache. */
time_t timeout = cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh, time_t timeout = cache_addpw (db, fd, req, keystr, pwd, c_uid, he, dh,
errval); errval);
scratch_buffer_free (&tmpbuf);
if (use_malloc)
free (buffer);
return timeout; return timeout;
} }

View file

@ -16,7 +16,6 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>. */ along with this program; if not, see <http://www.gnu.org/licenses/>. */
#include <alloca.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <libintl.h> #include <libintl.h>
@ -25,6 +24,7 @@
#include <stdint.h> #include <stdint.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <kernel-features.h> #include <kernel-features.h>
#include <scratch_buffer.h>
#include "nscd.h" #include "nscd.h"
#include "dbg_log.h" #include "dbg_log.h"
@ -346,12 +346,11 @@ addservbyX (struct database_dyn *db, int fd, request_header *req,
look again in the table whether the dataset is now available. We look again in the table whether the dataset is now available. We
simply insert it. It does not matter if it is in there twice. The simply insert it. It does not matter if it is in there twice. The
pruning function only will look at the timestamp. */ pruning function only will look at the timestamp. */
size_t buflen = 1024;
char *buffer = (char *) alloca (buflen);
struct servent resultbuf; struct servent resultbuf;
struct servent *serv; struct servent *serv;
bool use_malloc = false;
int errval = 0; int errval = 0;
struct scratch_buffer tmpbuf;
scratch_buffer_init (&tmpbuf);
if (__glibc_unlikely (debug_level > 0)) if (__glibc_unlikely (debug_level > 0))
{ {
@ -361,43 +360,24 @@ addservbyX (struct database_dyn *db, int fd, request_header *req,
dbg_log (_("Reloading \"%s\" in services cache!"), key); dbg_log (_("Reloading \"%s\" in services cache!"), key);
} }
while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0 while (lookup (req->type, key, &resultbuf,
tmpbuf.data, tmpbuf.length, &serv) != 0
&& (errval = errno) == ERANGE) && (errval = errno) == ERANGE)
{ if (!scratch_buffer_grow (&tmpbuf))
errno = 0; {
/* We ran out of memory. We cannot do anything but sending a
if (__glibc_unlikely (buflen > 32768)) negative response. In reality this should never
{ happen. */
char *old_buffer = buffer; serv = NULL;
buflen *= 2; /* We set the error to indicate this is (possibly) a temporary
buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen); error and that it does not mean the entry is not available
if (buffer == NULL) at all. */
{ errval = EAGAIN;
/* We ran out of memory. We cannot do anything but break;
sending a negative response. In reality this should }
never happen. */
serv = NULL;
buffer = old_buffer;
/* We set the error to indicate this is (possibly) a
temporary error and that it does not mean the entry
is not available at all. */
errval = EAGAIN;
break;
}
use_malloc = true;
}
else
/* Allocate a new buffer on the stack. If possible combine it
with the previously allocated buffer. */
buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
}
time_t timeout = cache_addserv (db, fd, req, key, serv, uid, he, dh, errval); time_t timeout = cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
scratch_buffer_free (&tmpbuf);
if (use_malloc)
free (buffer);
return timeout; return timeout;
} }