Skip to content

Commit

Permalink
Add a canary value to FileNode
Browse files Browse the repository at this point in the history
Adds a uint32 value to FileNode that is initialized to an
arbitrary value (CANARY_OK) in the constructor, and reset
when the reference is dropped (CANARY_RELEASED,
CANARY_DESTROYED).

The canary is checked on each withFileNode call.

Makes it much easier to trigger the bug seen in
#214 .
  • Loading branch information
rfjakob committed Jul 19, 2017
1 parent 319f7b4 commit c8ff1f9
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 0 deletions.
3 changes: 3 additions & 0 deletions encfs/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,13 @@ void EncFS_Context::eraseNode(const char *path, FileNode *pl) {
FileMap::iterator it = openFiles.find(std::string(path));
rAssert(it != openFiles.end());

auto fn = it->second.front();

it->second.pop_front();

// if no more references to this file, remove the record all together
if (it->second.empty()) {
fn->canary = CANARY_RELEASED;
openFiles.erase(it);
}
}
Expand Down
3 changes: 3 additions & 0 deletions encfs/FileNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg,

Lock _lock(mutex);

this->canary = CANARY_OK;

this->_pname = plaintextName_;
this->_cname = cipherName_;
this->parent = parent_;
Expand All @@ -76,6 +78,7 @@ FileNode::~FileNode() {
// FileNode mutex should be locked before the destructor is called
// pthread_mutex_lock( &mutex );

canary = CANARY_DESTROYED;
_pname.assign(_pname.length(), '\0');
_cname.assign(_cname.length(), '\0');
io.reset();
Expand Down
6 changes: 6 additions & 0 deletions encfs/FileNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
#include "FileUtils.h"
#include "encfs.h"

#define CANARY_OK 0x46040975
#define CANARY_RELEASED 0x70c5610d
#define CANARY_DESTROYED 0x52cdad90

namespace encfs {

class Cipher;
Expand All @@ -45,6 +49,8 @@ class FileNode {
const char *cipherName);
~FileNode();

uint32_t canary;

const char *plaintextName() const;
const char *cipherName() const;

Expand Down
10 changes: 10 additions & 0 deletions encfs/encfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ static int withFileNode(const char *opName, const char *path,

auto do_op = [&FSRoot, opName, &op](FileNode *fnode) {
rAssert(fnode != nullptr);
if(fnode->canary != CANARY_OK) {
if(fnode->canary == CANARY_RELEASED) {
RLOG(ERROR) << "canary=CANARY_RELEASED. File node accessed after it was released.";
} else if(fnode->canary == CANARY_DESTROYED) {
RLOG(ERROR) << "canary=CANARY_DESTROYED. File node accessed after it was destroyed.";
} else {
RLOG(ERROR) << "canary=0x" << std::hex << fnode->canary << ". Corruption?";
}
throw Error("dead canary");
}
VLOG(1) << "op: " << opName << " : " << fnode->cipherName();

// check that we're not recursing into the mount point itself
Expand Down

4 comments on commit c8ff1f9

@benrubson
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rfjakob will this commit definitely remain in the code ?
Or is it let's say some debug code while #214 is not solved ? (this is how it sounds to me, this is why I ask this question :)
Thx 👍

@rfjakob
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to keep this because it catches memory corruption and use-after-free bugs early. It's not a fix to #214, it just makes reproducing the problem much easier.

@jiangjianping
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rfjakob

I easily encounter the core dump. From valgrind output, It seems that the accessed filenode is released. But the canary log did not trigger the bug. I hope I do something to improve the code, please tell me how to do that?
valgrind output:
==5544== Thread 5:
==5544== Invalid read of size 8
==5544== at 0x5A62BE0: std::__cxx11::basic_string<char, std::char_traits, std::allocator >::c_str() const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==5544== by 0x53785D: encfs::FileNode::cipherName() const (in /opt/zjcloud/sbin/encfs)
==5544== by 0x52739D: encfs::withFileNode(char const*, char const*, fuse_file_info*, std::function<int (encfs::FileNode*)>)::{lambda(encfs::FileNode*)#1}::operator()(encfs::FileNode*) const (in /opt/zjcloud/sbin/
encfs)
==5544== by 0x5276D7: encfs::withFileNode(char const*, char const*, fuse_file_info*, std::function<int (encfs::FileNode*)>) (in /opt/zjcloud/sbin/encfs)
==5544== by 0x52A7E8: encfs::encfs_flush(char const*, fuse_file_info*) (in /opt/zjcloud/sbin/encfs)
==5544== by 0x4E494E6: fuse_flush_common (fuse.c:3844)
==5544== by 0x4E4976F: fuse_lib_flush (fuse.c:3894)
==5544== by 0x4E4FDB5: do_flush (fuse_lowlevel.c:1322)
==5544== by 0x4E50CE0: fuse_ll_process_buf (fuse_lowlevel.c:2443)
==5544== by 0x4E4D477: fuse_do_work (fuse_loop_mt.c:117)
==5544== by 0x572C6B9: start_thread (pthread_create.c:333)
==5544== by 0x5FE13DC: clone (clone.S:109)
==5544== Address 0x8cfa000 is 112 bytes inside a block of size 152 free'd
==5544== at 0x4C2F1A0: operator delete(void*) (vg_replace_malloc.c:576)
==5544== by 0x5267D7: std::_Sp_counted_ptr<encfs::FileNode*, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (in /opt/zjcloud/sbin/encfs)
==5544== by 0x4EAE69: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (in /opt/zjcloud/sbin/encfs)
==5544== by 0x4EA5DC: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count() (in /opt/zjcloud/sbin/encfs)
==5544== by 0x51B2CB: std::__shared_ptr<encfs::FileNode, (__gnu_cxx::_Lock_policy)2>::
__shared_ptr() (in /opt/zjcloud/sbin/encfs)
==5544== by 0x51B30D: std::shared_ptrencfs::FileNode::~shared_ptr() (in /opt/zjcloud/sbin/encfs)
==5544== by 0x51AAC5: encfs::EncFS_Context::eraseNode(char const*, encfs::FileNode*) (in /opt/zjcloud/sbin/encfs)
==5544== by 0x52A88B: encfs::encfs_release(char const*, fuse_file_info*) (in /opt/zjcloud/sbin/encfs)
==5544== by 0x4E46DC1: fuse_do_release (fuse.c:3091)
==5544== by 0x4E49642: fuse_lib_release (fuse.c:3879)
==5544== by 0x4E50018: do_release (fuse_lowlevel.c:1346)
==5544== by 0x4E50CE0: fuse_ll_process_buf (fuse_lowlevel.c:2443)
==5544== Block was alloc'd at
==5544== at 0x4C2E216: operator new(unsigned long) (vg_replace_malloc.c:334)
==5544== by 0x52346E: encfs::DirNode::findOrCreate(char const*) (in /opt/zjcloud/sbin/encfs)
==5544== by 0x523854: encfs::DirNode::openNode(char const*, char const*, int, int*) (in /opt/zjcloud/sbin/encfs)
==5544== by 0x52A3EC: encfs::encfs_open(char const*, fuse_file_info*) (in /opt/zjcloud/sbin/encfs)
==5544== by 0x4E47167: fuse_compat_open (fuse.c:1474)
==5544== by 0x4E47167: fuse_fs_open (fuse.c:1739)
==5544== by 0x4E47241: fuse_lib_open (fuse.c:3215)
==5544== by 0x4E515BB: do_open (fuse_lowlevel.c:1214)
==5544== by 0x4E50CE0: fuse_ll_process_buf (fuse_lowlevel.c:2443)
==5544== by 0x4E4D477: fuse_do_work (fuse_loop_mt.c:117)
==5544== by 0x572C6B9: start_thread (pthread_create.c:333)
==5544== by 0x5FE13DC: clone (clone.S:109)

@jiangjianping
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rfjakob
I just test the gocryptfs, It worked very well in our testing environment. It's great!

Please sign in to comment.