[2434] in Kerberos-V5-bugs
krb5-libs/174: Fix for infinitely growing replay cache
daemon@ATHENA.MIT.EDU (Ken Hornstein)
Sun Nov 10 23:18:22 1996
Resent-From: gnats@rt-11.MIT.EDU (GNATS Management)
Resent-To: krb5-unassigned@RT-11.MIT.EDU
Resent-Reply-To: krb5-bugs@MIT.EDU, kenh@cmf.nrl.navy.mil
Date: Sun, 10 Nov 1996 23:16:44 -0500 (EST)
From: Ken Hornstein <kenh@cmf.nrl.navy.mil>
Reply-To: kenh@cmf.nrl.navy.mil
To: krb5-bugs@MIT.EDU
>Number: 174
>Category: krb5-libs
>Synopsis: A fix for the problem where the reply cache grows without bounds
>Confidential: no
>Severity: critical
>Priority: high
>Responsible: krb5-unassigned
>State: open
>Class: sw-bug
>Submitter-Id: unknown
>Arrival-Date: Sun Nov 10 23:18:00 EST 1996
>Last-Modified:
>Originator: Ken Hornstein
>Organization:
Naval Research Lab
>Release: beta-7
>Environment:
System: SunOS nexus 4.1.4 1 sun4m
Architecture: sun4
>Description:
The replay cache code automatically prunes the replay cache if too many
entries are in a hash bucket. This works great for long-living daemons
like the KDC.
However, for daemons that start up once, do one authentication, then exit,
the replay cache will grow without bounds. Eventually noticable delays
will incur as each invokation of the daemon requires it to read in the
entire replay cache.
>How-To-Repeat:
Retrieve your mail via KPOP, and notice over a period of time that it keeps
taking longer and longer ...
>Fix:
This is a rework of the patch that Jonathan Kamens posted a little while
back. His patch was for beta 4 (I think) -- this is done up for beta 7.
I've been running with this for a couple of weeks now with no ill effects.
I just wanted to resend this so anyone who needed a fix could put it in place
now.
--- lib/krb5/rcache/rc_dfl.c.orig Mon Nov 27 15:51:53 1995
+++ lib/krb5/rcache/rc_dfl.c Wed Oct 23 15:19:34 1996
@@ -54,7 +54,11 @@
of live krb5_donot_replays by EXCESSREPS. With the defaults here, a typical
cache might build up some 10K of expired krb5_donot_replays before an automatic
expunge, with the waste basically independent of the number of stores per
-minute. */
+minute.
+
+The rcache will also automatically be expunged when it encounters more
+than EXCESSREPS expired entries when recovering a cache in
+dfl_recover. */
static int hash(rep, hsize)
krb5_donot_replay *rep;
@@ -110,6 +114,7 @@
#ifndef NOIOSTUFF
krb5_rc_iostuff d;
#endif
+ char recovering;
}
;
@@ -281,6 +286,7 @@
#ifndef NOIOSTUFF
t->d.fd = -1;
#endif
+ t->recovering = 0;
return 0;
cleanup:
@@ -388,12 +394,15 @@
#else
struct dfl_data *t = (struct dfl_data *)id->data;
- krb5_donot_replay *rep;
+ krb5_donot_replay *rep = 0;
krb5_error_code retval;
long max_size;
+ int expired_entries = 0;
if ((retval = krb5_rc_io_open(context, &t->d, t->name)))
return retval;
+
+ t->recovering = 1;
max_size = krb5_rc_io_size(context, &t->d);
@@ -429,6 +438,8 @@
if (rc_store(context, id, rep) == CMP_MALLOC) {
retval = KRB5_RC_MALLOC; goto io_fail;
}
+ } else {
+ expired_entries++;
}
/*
* free fields allocated by rc_io_fetch
@@ -448,6 +459,9 @@
krb5_rc_free_entry(context, &rep);
if (retval)
krb5_rc_io_close(context, &t->d);
+ else if (expired_entries > EXCESSREPS)
+ retval = krb5_rc_dfl_expunge(context, id);
+ t->recovering = 0;
return retval;
#endif
@@ -461,7 +475,7 @@
{
int clientlen, serverlen, len;
char *buf, *ptr;
- unsigned long ret;
+ krb5_error_code ret;
clientlen = strlen (rep->client) + 1;
serverlen = strlen (rep->server) + 1;
@@ -488,7 +502,7 @@
krb5_rcache id;
krb5_donot_replay *rep;
{
- unsigned long ret;
+ krb5_error_code ret;
struct dfl_data *t = (struct dfl_data *)id->data;
switch(rc_store(context, id,rep)) {
@@ -556,17 +570,20 @@
krb5_rcache tmp;
krb5_deltat lifespan = t->lifespan; /* save original lifespan */
- name = t->name;
- t->name = 0; /* Clear name so it isn't freed */
- (void) krb5_rc_dfl_close_no_free(context, id);
- retval = krb5_rc_dfl_resolve(context, id, name);
- free(name);
- if (retval)
- return retval;
- retval = krb5_rc_dfl_recover(context, id);
- if (retval)
- return retval;
- t = (struct dfl_data *)id->data; /* point to recovered cache */
+ if (! t->recovering) {
+ name = t->name;
+ t->name = 0; /* Clear name so it isn't freed */
+ (void) krb5_rc_dfl_close_no_free(context, id);
+ retval = krb5_rc_dfl_resolve(context, id, name);
+ free(name);
+ if (retval)
+ return retval;
+ retval = krb5_rc_dfl_recover(context, id);
+ if (retval)
+ return retval;
+ t = (struct dfl_data *)id->data; /* point to recovered cache */
+ }
+
tmp = (krb5_rcache) malloc(sizeof(*tmp));
if (!tmp)
return ENOMEM;
>Audit-Trail:
>Unformatted: