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

reconstruct failures #16

Closed
elliefm opened this issue Aug 15, 2016 · 19 comments
Closed

reconstruct failures #16

elliefm opened this issue Aug 15, 2016 · 19 comments
Assignees
Labels
3.0 affects 3.0

Comments

@elliefm
Copy link
Contributor

elliefm commented Aug 15, 2016

First, let's see what we have. Ignore the folder names, these are (still) just ordinary folders. Stumbled across this while setting up something unrelated.

$ cyr_dbtool /cyrus/slot/frodo/var/imap/mailboxes.db twoskip show user.cassandane
user.cassandane %(A %(cassandane lrswipkxtecdan) I ca64cac3-b498-4008-9e84-95b76313ca6a P default V 1471226865 F 1 M 1471226864)
user.cassandane.Trash %(A %(cassandane lrswipkxtecdan) I d90ef08e-be76-41ab-bfed-289b1fbfac90 P default V 1471227100 F 1 M 1471235722)
user.cassandane.version10 %(A %(cassandane lrswipkxtecdan) I b82ff5f8-93fb-486e-b1aa-f90fe7dca7c3 P default V 1471228241 F 1 M 1471235722)
user.cassandane.version12 %(A %(cassandane lrswipkxtecdan) I 5a9ea7d3-a983-464a-8dd0-ce55652920f2 P default V 1471228261 F 1 M 1471235722)
user.cassandane.version9 %(A %(cassandane lrswipkxtecdan) I d06678d8-ea72-4147-9a7f-92225ab180b5 P default V 1471228252 F 1 M 1471235722)

Cool. Let's reconstruct user.cassandane:

$ reconstruct user.cassandane
user.cassandane
Segmentation fault (core dumped)

Uh oh? Likewise with -r (recursive):

$ reconstruct -r user.cassandane
user.cassandane
Segmentation fault (core dumped)

What if I use a pattern?

$ reconstruct user.cassandane.*
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9

Okay that's better, but it's missed the Inbox because of that "."...

$ reconstruct user.cassandane*
user.cassandane
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9

Okay that's done the trick. (Lucky we don't have some other user called "user.cassandane2" or something....) What about recursively?

$ reconstruct -r user.cassandane*
user.cassandane
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9

Hmm, it's done all the subfolders twice, I suppose because they all match the pattern but they also exist under user.cassandane. What if we exclude the top level?

$ reconstruct -r user.cassandane.*
user.cassandane.Trash: update uniqueid from header 4904b2a3-e244-496b-98b3-5290c97774a7 => d90ef08e-be76-41ab-bfed-289b1fbfac90
user.cassandane.Trash
user.cassandane.version10: update uniqueid from header f281d4cb-ef62-4d53-9e7a-a3ee726300a4 => b82ff5f8-93fb-486e-b1aa-f90fe7dca7c3
user.cassandane.version10
user.cassandane.version12: update uniqueid from header c3e3de31-be02-4a28-9247-b2d19dbab32e => 5a9ea7d3-a983-464a-8dd0-ce55652920f2
user.cassandane.version12
user.cassandane.version9: update uniqueid from header 18df9b02-a2cf-41d3-b6e9-5853c6bf08f0 => d06678d8-ea72-4147-9a7f-92225ab180b5
user.cassandane.version9

Whoa what the hell? Syslog says:

Aug 15 14:35:18 debian-testing frodo/reconstruct[15182]: uniqueid clash with user.cassandane.Trash for 4904b2a3-e244-496b-98b3-5290c97774a7 - changing user.cassandane.Trash
Aug 15 14:35:18 debian-testing frodo/reconstruct[15182]: reconstructing user.cassandane.version10
Aug 15 14:35:18 debian-testing frodo/reconstruct[15182]: uniqueid clash with user.cassandane.version10 for f281d4cb-ef62-4d53-9e7a-a3ee726300a4 - changing user.cassandane.version10
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]: reconstructing user.cassandane.version12
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]: uniqueid clash with user.cassandane.version12 for c3e3de31-be02-4a28-9247-b2d19dbab32e - changing user.cassandane.version12
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]: reconstructing user.cassandane.version9
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]: uniqueid clash with user.cassandane.version9 for 18df9b02-a2cf-41d3-b6e9-5853c6bf08f0 - changing user.cassandane.version9

Playing around, that latter case looks like it only crops up after a reconstruct of "user.cassandane*" (the one that reconstructs all the subfolders twice), so it /might/ actually be fixing something that the earlier reconstruct broke.

So two problems, probably unrelated:

  1. reconstruct segfaults when run directly on a user's Inbox
  2. double-handling of mailboxes that messes up the uniqueids in some way
@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

This is against master at 2072f6c. Backtrace from the segmentation fault:

Core was generated by `/cyrus/slot/frodo/usr/cyrus/sbin/reconstruct user.cassandane'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fc59813c161 in mbname_intname (mbname=0x0) at imap/mboxname.c:788
788     if (mbname->intname)
(gdb) bt
#0  0x00007fc59813c161 in mbname_intname (mbname=0x0) at imap/mboxname.c:788
#1  0x0000000000402a15 in do_reconstruct (data=0x7ffe5d25fb10, rock=0x0) at imap/reconstruct.c:440
#2  0x00007fc598134456 in find_cb (rockp=0x7ffe5d260a10, 
    key=0x8a6bf0 "user.cassandane.Trashhild.grandchildgrandchildsoon", keylen=21, 
    data=0x7fc5985c7805 <error: Cannot access memory at address 0x7fc5985c7805>, datalen=112)
    at imap/mboxlist.c:2385
#3  0x00007fc597d08bcf in myforeach (db=0x8a5d50, prefix=0x7ffe5d260150 "user.cassandane", 
    prefixlen=0, goodp=0x7fc598133f4f <find_p>, cb=0x7fc598134210 <find_cb>, rock=0x7ffe5d260a10, 
    tidptr=0x0) at lib/cyrusdb_twoskip.c:1582
#4  0x00007fc597cfa82b in cyrusdb_foreach (db=0x8a5ad0, prefix=0x7ffe5d260150 "user.cassandane", 
    prefixlen=0, p=0x7fc598133f4f <find_p>, cb=0x7fc598134210 <find_cb>, rock=0x7ffe5d260a10, 
    tid=0x0) at lib/cyrusdb.c:237
#5  0x00007fc59813510c in mboxlist_find_category (rock=0x7ffe5d260a10, 
    prefix=0x7ffe5d260150 "user.cassandane", len=0) at imap/mboxlist.c:2660
#6  0x00007fc59813591a in mboxlist_do_find (rock=0x7ffe5d260a10, patterns=0x7ffe5d260af0)
    at imap/mboxlist.c:2829
#7  0x00007fc598135ba5 in mboxlist_findallmulti (namespace=0x603ba0 <recon_namespace>, 
    patterns=0x7ffe5d260af0, isadmin=1, userid=0x0, auth_state=0x0, 
    proc=0x4029c2 <do_reconstruct>, rock=0x0) at imap/mboxlist.c:2894
#8  0x00007fc598135c21 in mboxlist_findall (namespace=0x603ba0 <recon_namespace>, 
    pattern=0x7ffe5d260b50 "user.cassandane", isadmin=1, userid=0x0, auth_state=0x0, 
    proc=0x4029c2 <do_reconstruct>, rock=0x0) at imap/mboxlist.c:2907
#9  0x00000000004026e6 in main (argc=2, argv=0x7ffe5d261c98) at imap/reconstruct.c:340

And analysis so far, pasted from IRC:

1:03 "user.cassandane.Trashhild.grandchildgrandchildsoon" looks like some kind of memory corruption though
1:04 though keylen=21 is good, it's just not \0'd, hmm
2:04 find_cb() in mboxlist.c is initialising the mbname field of struct findall_data to NULL
2:06 it then only sets it to anything else if rock->matchlen == strlen(extname) — which in this case is not true, so it doesn't
2:06 then it passes that struct findall_data to do_reconstruct() in reconstruct.c
2:06 which immediately tries to call mbname_intname(data->mbname)
2:06 — which is NULL —
2:07 and so mbname_intname() segfaults upon trying to deref its ->intname
2:08 do_reconstruct() expects mbname_intname to return a valid char *, so if we made mbname_intname return NULL when its argument is NULL, we would just move the segfault to somewhere else
2:09 there's something fishy about that rock->matchlen==strlen(extname) check, or rather, the apparent lack of dealing with the possibility that it doesn't

@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

This seems to be a totally different issue from https://bugzilla.cyrusimap.org/show_bug.cgi?id=3919

@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

The struct findall_data implementation, and the crash, were introduced in d6e6f90 -- ping @brong?

@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

11772bb fixes the double handling of mailboxes by keeping a hash of the visited mailboxes, and skipping anything that's already in it. Looks like we used to already do something like this, but could only remember one visited item, so if it ended up trying to process A, A, then A would be skipped the second time, but if it was trying to process A, B, A, then A would get processed twice.

@brong
Copy link
Member

brong commented Aug 15, 2016

On Mon, 15 Aug 2016, at 14:57, elliefm wrote:

First, let's see what we have. Ignore the folder names, these are
(still) just ordinary folders. Stumbled across this while setting up
something unrelated.

$ cyr_dbtool /cyrus/slot/frodo/var/imap/mailboxes.db twoskip show
user.cassandane
user.cassandane %(A %(cassandane lrswipkxtecdan) I ca64cac3-b498-4008-9e84-
95b76313ca6a P default V 1471226865 F 1 M 1471226864)
user.cassandane.Trash %(A %(cassandane lrswipkxtecdan) I d90ef08e-be76-41ab-bfed-
289b1fbfac90 P default V 1471227100 F 1 M 1471235722)
user.cassandane.version10 %(A %(cassandane lrswipkxtecdan) I b82ff5f8-93fb-486e-b1aa-
f90fe7dca7c3 P default V 1471228241 F 1 M 1471235722)
user.cassandane.version12 %(A %(cassandane lrswipkxtecdan) I 5a9ea7d3-a983-464a-8dd0-
ce55652920f2 P default V 1471228261 F 1 M 1471235722)
user.cassandane.version9 %(A %(cassandane lrswipkxtecdan) I d06678d8-ea72-4147-9a7f-
92225ab180b5 P default V 1471228252 F 1 M 1471235722)
Cool. Let's reconstruct user.cassandane:
$ reconstruct user.cassandane
user.cassandane
Segmentation fault (core dumped)
OK, that's clearly broken.
Uh oh? Likewise with -r (recursive):
$ reconstruct -r user.cassandane
user.cassandane
Segmentation fault (core dumped)
What if I use a pattern?
$ reconstruct user.cassandane.*
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9
Okay that's better, but it's missed the Inbox because of that "."...
$ reconstruct user.cassandane*
user.cassandane
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9
Okay that's done the trick. (Lucky we don't have some other user
called "user.cassandane2" or something....) What about recursively?
$ reconstruct -r user.cassandane*
user.cassandane
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9
user.cassandane.Trash
user.cassandane.version10
user.cassandane.version12
user.cassandane.version9
Hmm, it's done all the subfolders twice, I suppose because they all
match the pattern but they also exist under user.cassandane. What if
we exclude the top level?

That's also a bug, we shouldn't allow this to happen. -r shouldn't use
wildcards. Chances are this screwed up all your uniqueids because it
would have found clashes and recreated the uniqueid.

$ reconstruct -r user.cassandane.*
user.cassandane.Trash: update uniqueid from header 4904b2a3-e244-496b-98b3-
5290c97774a7 => d90ef08e-be76-41ab-bfed-289b1fbfac90
user.cassandane.Trash

Leading to this - a brand new bug. Creating new uniqueids due to a
clash shouldn't have left the mailboxes in a broken state.

user.cassandane.version10: update uniqueid from header f281d4cb-ef62-4d53-9e7a-
a3ee726300a4 => b82ff5f8-93fb-486e-b1aa-f90fe7dca7c3
user.cassandane.version10
user.cassandane.version12: update uniqueid from header c3e3de31-be02-4a28-9247-
b2d19dbab32e => 5a9ea7d3-a983-464a-8dd0-ce55652920f2
user.cassandane.version12
user.cassandane.version9: update uniqueid from header 18df9b02-a2cf-41d3-b6e9-
5853c6bf08f0 => d06678d8-ea72-4147-9a7f-92225ab180b5
user.cassandane.version9
Whoa what the hell? Syslog says:
Aug 15 14:35:18 debian-testing frodo/reconstruct[15182]: uniqueid
clash with user.cassandane.Trash for 4904b2a3-e244-496b-98b3-
5290c97774a7 - changing user.cassandane.Trash
Aug 15 14:35:18 debian-testing frodo/reconstruct[15182]:
reconstructing user.cassandane.version10
Aug 15 14:35:18 debian-testing frodo/reconstruct[15182]: uniqueid
clash with user.cassandane.version10 for f281d4cb-ef62-4d53-9e7a-
a3ee726300a4 - changing user.cassandane.version10
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]:
reconstructing user.cassandane.version12
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]: uniqueid
clash with user.cassandane.version12 for c3e3de31-be02-4a28-9247-
b2d19dbab32e - changing user.cassandane.version12
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]:
reconstructing user.cassandane.version9
Aug 15 14:35:19 debian-testing frodo/reconstruct[15182]: uniqueid
clash with user.cassandane.version9 for 18df9b02-a2cf-41d3-b6e9-
5853c6bf08f0 - changing user.cassandane.version9
Playing around, that latter case looks like it only crops up after a
reconstruct of "user.cassandane*" (the one that reconstructs all the
subfolders twice), so it /might/ actually be fixing something that the
earlier reconstruct broke.

Yep, it is.

So two problems, probably unrelated:

  1. reconstruct segfaults when run directly on a user's Inbox

Yeah, that's bogus. What happens if you run 'reconstruct -u
$username' ?

  1. double-handling of mailboxes that messes up the uniqueids in
    some way

That's definitely bogus - it shouldn't be possible to double-handle
mailboxes, because we keep a hash of seen uniqueids, and as you saw, the
"fixup" code wasn't very fixy.

Bron Gondwana
brong@fastmail.fm

@brong
Copy link
Member

brong commented Aug 15, 2016

On Mon, 15 Aug 2016, at 17:00, elliefm wrote:

11772bb[1] fixes the double handling of mailboxes by keeping a hash of
the visited mailboxes, and skipping anything that's already in it.
Looks like we used to already do something like this, but could only
remember one visited item, so if it ended up trying to process A, A,
then A would be skipped the second time, but if it was trying to
process A, B, A, then A would get processed twice.

I am satisfied that this is generally a good belt-and-suspenders even

once we fix the stupid -r behaviour.

Bron Gondwana
brong@fastmail.fm

Links:

  1. 11772bb

@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

IIRC I think reconstruct -u username worked okay, but I'll verify tomorrow when I have my vm up again

@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

I've just painstakingly stepped through reconstruct -u cassandane and it correctly finds the user.cassandane mailboxes and starts iterating through them...

... but for each one encountered, find_p ends up rejecting it, so nothing happens.

(gdb) print *rock
$10 = {globs = {count = 0, alloc = 0, data = 0x0}, 
  namespace = 0x603c20 <recon_namespace>, userid = 0x0, domain = 0x0, mb_category = 0, 
  checkmboxlist = 0, issubs = 0, singlepercent = 0, db = 0x638ad0, isadmin = 1, 
  auth_state = 0x0, mbname = 0x639b60, mbentry = 0x0, matchlen = 0, 
  proc = 0x402a33 <do_reconstruct>, procrock = 0x0}
(gdb) print *rock->mbname
$11 = {boxes = 0x639c00, is_deleted = 0, localpart = 0x6398b0 "cassandane", 
  domain = 0x0, extns = 0x0, extuserid = 0x0, userid = 0x0, 
  intname = 0x639cb0 "user.cassandane.Trash", extname = 0x0, recipient = 0x0}
(gdb) n
2279        if (mbname_category(rock->mbname, rock->namespace, rock->userid) != rock->mb_category)
(gdb) n
2280            goto nomatch;

Note that rock->mb_category is zero. I'm not sure what this means yet.

Stepping through this though, I have noticed that I missed passing the reconstruct_rock to do_reconstruct_p() rock in my last fix, so if it did manage to get through as far as do_reconstruct() it would promptly crash out, doh! So I'll fix that, and then see if I can see what this mb_category business is about.

@brong
Copy link
Member

brong commented Aug 15, 2016

On Tue, 16 Aug 2016, at 09:03, elliefm wrote:

I've just painstakingly stepped through reconstruct -u cassandane and
it correctly finds the user.cassandane mailboxes and starts iterating
through them...
... but for each one encountered, find_p ends up rejecting it, so
nothing happens.

(gdb) print *rock $10 = {globs = {count = 0, alloc = 0, data = 0x0},
namespace = 0x603c20 <recon_namespace>, userid = 0x0, domain = 0x0,
mb_category = 0, checkmboxlist = 0, issubs = 0, singlepercent = 0, db
= 0x638ad0, isadmin = 1, auth_state = 0x0, mbname = 0x639b60, mbentry
= 0x0, matchlen = 0, proc = 0x402a33 <do_reconstruct>, procrock = 0x0}
(gdb) print *rock->mbname $11 = {boxes = 0x639c00, is_deleted = 0,
localpart = 0x6398b0 "cassandane", domain = 0x0, extns = 0x0,
extuserid = 0x0, userid = 0x0, intname = 0x639cb0
"user.cassandane.Trash", extname = 0x0, recipient = 0x0} (gdb) n 2279
if (mbname_category(rock->mbname, rock->namespace, rock->userid) !=
rock->mb_category) (gdb) n 2280 goto nomatch;

Note that rock->mb_category is zero. I'm not sure what this
means yet.
Stepping through this though, I have noticed that I missed passing the
reconstruct_rock to do_reconstruct_p() rock in my last fix, so if it
did manage to get through as far as do_reconstruct() it would promptly
crash out, doh! So I'll fix that, and then see if I can see what this
mb_category business is about.

Ahh, yeah - OK - that could explain some bits. mb_category is
this thing:

/* categorise mailboxes */
enum { MBNAME_INBOX = 1,
MBNAME_INBOXSUB = 2,
MBNAME_ALTINBOX = 3,
MBNAME_ALTPREFIX = 4,
MBNAME_OWNER = 5,
MBNAME_OTHERUSER = 6,
MBNAME_SHARED = 7 };

So what we probably want there is something like:

if (rock->mb_category && mbname_category(rock->mbname, rock->namespace,
rock->userid) != rock->mb_category)

So that in the zero case, all mailboxes match.

Bron.

Bron Gondwana
brong@fastmail.fm

@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

That makes sense

For what it's worth, mbname_category(...) is returning MBNAME_OTHERUSER in this case, because:

mbname_category (mbname=0x639b60, ns=0x603c20 <recon_namespace>, userid=0x0)
    at imap/mboxname.c:920
920     if (!mbname_localpart(mbname)) return MBNAME_SHARED;
(gdb) print *mbname
$3 = {boxes = 0x639be0, is_deleted = 0, localpart = 0x639cb0 "cassandane", domain = 0x0, 
  extns = 0x0, extuserid = 0x0, userid = 0x0, intname = 0x6399e0 "user.cassandane", 
  extname = 0x0, recipient = 0x0}
(gdb) n
921     if (mbname_isdeleted(mbname)) return MBNAME_SHARED;
(gdb) n
923     if (strcmpsafe(mbname_userid(mbname), userid)) return MBNAME_OTHERUSER;
(gdb) n
950 }

The userid passed in is NULL, which doesn't match "cassandane" as determined by mbname_userid(mbname). I'm interpreting the "userid" parameter as "the user performing this operation"? Which in this case would logically be CYRUS_USER I suppose, since we're in a system tool, not on a socket. So that makes sense actually.

@elliefm
Copy link
Contributor Author

elliefm commented Aug 15, 2016

After d15fcce it still doesn't make it through find_p positively:

2289        int matchlen = 0;
(gdb) 
2290        for (i = 0; i < rock->globs.count; i++) {
(gdb) print *rock
$7 = {globs = {count = 0, alloc = 0, data = 0x0}, 
  namespace = 0x603c20 <recon_namespace>, userid = 0x0, domain = 0x0, mb_category = 0, 
  checkmboxlist = 0, issubs = 0, singlepercent = 0, db = 0x638ad0, isadmin = 1, 
  auth_state = 0x0, mbname = 0x639b60, mbentry = 0x0, matchlen = 0, 
  proc = 0x402a35 <do_reconstruct>, procrock = 0x7fffffffd9f0}
(gdb) n
2297        if (!matchlen) goto nomatch;
(gdb) print matchlen
$8 = 0

The struct find_rock is initialised by mboxlist_findone -- maybe it should add a glob?
Or maybe find_p:2297 should be more like, if (rock->globs.count && !matchlen) goto nomatch;?

I've experimentally tried the latter, and reconstruct -u cassandane finally makes it into do_reconstruct for "user.cassandane" -- and then crashes in the exact same way as reconstruct user.cassandane, because find_cb initialises the struct findall_data's mbname field to NULL and doesn't update it unless rock->singlepercent is true (which in this case, it's not).

@elliefm
Copy link
Contributor Author

elliefm commented Aug 16, 2016

Okay so mboxlist_findone() looks like it can't ever work at the moment, because it sets up the struct find_rock without any globs, and then find_p rejects the only candidate because it doesn't match any globs.

It's used twice by reconstruct, once by autocreate, and once by chk_cyrus.

mboxlist_findone() gets an internal mailbox name as one of its arguments. find_p() matches the globs against an external mailbox name. So for mboxlist_findone() to set up a pattern, it would need to create the pattern in the external namespace -- which it could do, because it does get a namespace pointer. Though I'm not sure how to set up a pattern that matches a string exactly --

Okay nevermind that actually. mboxlist_findone() calls cyrusdb_forone(..., p=&find_p, cb=&find_cb, ...) which only ever finds a single element anyway, which it looks up by the given intname, so instead of jumping through hoops to make find_p also match exactly one item correctly, just pass NULL to cyrusdb_forone's p argument to bypass that check, sheesh.

@elliefm
Copy link
Contributor Author

elliefm commented Aug 16, 2016

But wait. find_p does some extra checks which maybe are important -- even if we've found the correct mailbox, it might decide it needs to pretend there is no such mailbox. Hmm.

@elliefm
Copy link
Contributor Author

elliefm commented Aug 16, 2016

Adding the pattern to mboxlist_findone appears to be the magic ticket -- it fixes the problem where find_p ignores the results, and then also fixes the problem where find_cb doesn't initialise findall_data.mbname, so we no longer crash out

@brong
Copy link
Member

brong commented Aug 16, 2016

great :)

On Tue, 16 Aug 2016, at 11:27, elliefm wrote:

Adding the pattern to mboxlist_findone appears to be the magic ticket
-- it fixes the problem where find_p ignores the results, and then
also fixes the problem where find_cb doesn't initialise
findall_data.mbname, so we no longer crash out
— You are receiving this because you were mentioned. Reply to this
email directly, view it on GitHub[1], or mute the thread[2].

Bron Gondwana
brong@fastmail.fm

Links:

  1. reconstruct failures #16 (comment)
  2. https://github.com/notifications/unsubscribe-auth/AABE7Ss931R36R5CCFQa4EAO-9fg7c3Zks5qgRH-gaJpZM4JkHOv

@elliefm
Copy link
Contributor Author

elliefm commented Aug 16, 2016

Need to test a couple more things, and needs some cleanup. But I have to head out for a bit, so WIP is here and I'll finish it off later: master...elliefm:v30/fix-reconstruct-crash

@elliefm
Copy link
Contributor Author

elliefm commented Aug 16, 2016

My wip patch fixes reconstruct -u cassandane but reconstruct user.cassandane still segfaults. Continuing to dig...

@elliefm
Copy link
Contributor Author

elliefm commented Aug 16, 2016

Okay so there's another thing going on here, and this is partially a user-interface specification issue:

When not using -u mode, reconstruct's arguments are understood as being mailbox patterns. We construct (with glob_init()) a pattern from each argument foo with heir_sep x thus: /(^foo)([x]|$)/ (plus a bit of handling for "*" and "%").

This means reconstruct user.cassandane, which looks like "reconstruct this single mailbox", ends up being the same as reconstruct -r user.cassandane, because every submailbox of user.cassandane matches /(^user\.cassandane)([.]|$)/. Even though we used no wildcards!

find_cb has some handling (rock->singlepercent) that looks like maybe it's trying to accommodate the possibility that we're matching on a pattern? Except that "user.cassandane" is a pattern, but contains no wildcards, so we don't end up setting rock->singlepercent.

We (now, with my patch) handle user.cassandane itself okay (because rock->matchlen == (int)strlen(extname), so we initialise fbdata.mbname). But for all the pattern-matched submailboxes we end up iterating over, this is not the case, so we end up back in do_reconstruct with no data->mbname, and we're crashing again.

[Aside: looking at the rock->singlepercent code path in find_cb(), fbdata.mbname isn't set there either. So do_reconstruct will probably crash in this case too, I just haven't tickled it yet.]

So again, a couple of aspects:

  1. Either find_cb() needs to be very careful to always initialise fbdata.mbname before calling rock->proc, or do_reconstruct() needs to accommodate the possibility of NULL data->mbname. I'm not sure which though.
  2. reconstruct ought to be clearer about when it treats its arguments as mailbox names vs mailbox prefixes vs mailbox patterns, and then stricter about how it implements these, so that names/prefixes don't end up treated badly as patterns and v/v.

@elliefm elliefm self-assigned this Aug 16, 2016
@elliefm
Copy link
Contributor Author

elliefm commented Aug 16, 2016

Turns out NULL fdata.mbname is find_cb's way of indicating partial matches to callers who need that, and other things that only care about exact matches just return 0 in this case. So reconstruct now does the same: e72821c

reconstruct now passes all the initial checks I threw at it, plus the -u username check discussed later.

There's still the matter of whether or not -r should accept wildcards or not -- at the moment it still does, but it at least handles them sensibly now instead of stomping all over itself.

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

No branches or pull requests

2 participants