Skip to content

LIST: list_next_channels() re-sends the hash bucket it paused on (duplicate RPL_LIST entries) #109

Description

@MrIron-no

Summary

When a LIST pauses because the client's sendq is over half full, the resumed tick re-sends the whole hash bucket it stopped on, so the client receives duplicate RPL_LIST (322) entries.

Where

ircd/hash.c, list_next_channels() (main, lines 436-472):

  for (args = cli_listing(cptr); args->bucket < HASHSIZE; args->bucket++)
  {
    /* Send all the matching channels in the bucket. */
    for (chptr = channelTable[args->bucket]; chptr; chptr = chptr->hnext)
    {
      ...
        send_reply(cptr, RPL_LIST, chptr->chname, chptr->users, chptr->topic);
      ...
    }
    /* If, at the end of the bucket, client sendq is more than half
     * full, stop. */
    if (MsgQLength(&cli_sendQ(cptr)) > get_sendq(cptr) / 2)
      break;
  }

The break fires after the bucket has been fully sent but before the loop's args->bucket++. When s_bsd.c later calls list_next_channels() again on a writable socket, the walk restarts at the same args->bucket and every matching channel in it is sent a second time.

Fix

Advance past the completed bucket before pausing:

    if (MsgQLength(&cli_sendQ(cptr)) > get_sendq(cptr) / 2)
    {
      args->bucket++;
      break;
    }

Reproduction

Observed while testing on a fork, on a connection whose kernel write actually blocked (client not reading, small SO_RCVBUF, connected directly rather than through a userspace proxy), with 1500 channels created over a server link and a class with sendq = 3000. The LIST reply contained 1552 322 lines: 1500 distinct channels and 52 repeats. The count of repeats is the number of channels in the buckets the listing paused on.

Note that on an ordinary connection this rarely shows: send_buffer() flushes to the kernel every 1KB, so the userspace sendq only crosses sendq/2 when the socket write itself blocks. Large LIST replies to slow clients are exactly the case where it will.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions