1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #ifndef SQUID_SRC_SECURITY_LOCKINGPOINTER_H
10 #define SQUID_SRC_SECURITY_LOCKINGPOINTER_H
11 
12 #include "base/HardFun.h"
13 
14 #if USE_OPENSSL
15 #include "compat/openssl.h"
16 #if HAVE_OPENSSL_CRYPTO_H
17 #include <openssl/crypto.h>
18 #endif
19 
20 // Macro to be used to define the C++ wrapper function of a sk_*_pop_free
21 // openssl family functions. The C++ function suffixed with the _free_wrapper
22 // extension
23 #define sk_free_wrapper(sk_object, argument, freefunction) \
24         extern "C++" inline void sk_object ## _free_wrapper(argument a) { \
25             sk_object ## _pop_free(a, freefunction); \
26         }
27 
28 #endif /* USE_OPENSSL */
29 
30 // Macro to be used to define the C++ equivalent function of an extern "C"
31 // function. The C++ function suffixed with the _cpp extension
32 #define CtoCpp1(function, argument) \
33         extern "C++" inline void function ## _cpp(argument a) { \
34             function(a); \
35         }
36 
37 namespace Security
38 {
39 
nilFunction(const void *)40 inline bool nilFunction(const void *) { return false; }
41 typedef HardFun<bool, const void *, nilFunction> NilFunctor;
42 
43 /**
44  * A shared pointer to a reference-counting Object with library-specific
45  * absorption, locking, and unlocking implementations. The API largely
46  * follows std::shared_ptr.
47  *
48  * The constructor and the resetWithoutLocking() method import a raw Object pointer.
49  * Normally, reset() would lock(), but libraries like OpenSSL
50  * pre-lock objects before they are fed to LockingPointer, necessitating
51  * this resetWithoutLocking() customization hook.
52  */
53 template <typename T, void (*UnLocker)(T *t), class Locker = NilFunctor>
54 class LockingPointer
55 {
56 public:
57     /// a helper label to simplify this objects API definitions below
58     typedef Security::LockingPointer<T, UnLocker, Locker> SelfType;
59 
60     /**
61      * Construct directly from a raw pointer.
62      * This action requires that the producer of that pointer has already
63      * created one reference lock for the object pointed to.
64      * Our destructor will do the matching unlock.
65      */
raw(nullptr)66     explicit LockingPointer(T *t = nullptr): raw(nullptr) {
67         // de-optimized for clarity about non-locking
68         resetWithoutLocking(t);
69     }
70 
71     /// use the custom UnLocker to unlock any value still stored.
~LockingPointer()72     ~LockingPointer() { unlock(); }
73 
74     // copy semantics are okay only when adding a lock reference
LockingPointer(const SelfType & o)75     LockingPointer(const SelfType &o) : raw(nullptr) {
76         resetAndLock(o.get());
77     }
78     const SelfType &operator =(const SelfType &o) {
79         resetAndLock(o.get());
80         return *this;
81     }
82 
LockingPointer(SelfType && o)83     LockingPointer(SelfType &&o) : raw(nullptr) {
84         resetWithoutLocking(o.release());
85     }
86     SelfType &operator =(SelfType &&o) {
87         if (o.get() != raw)
88             resetWithoutLocking(o.release());
89         return *this;
90     }
91 
92     bool operator !() const { return !raw; }
93     explicit operator bool() const { return raw; }
94     bool operator ==(const SelfType &o) const { return (o.get() == raw); }
95     bool operator !=(const SelfType &o) const { return (o.get() != raw); }
96 
97     T *operator ->() const { return raw; }
98 
99     /// Returns raw and possibly nullptr pointer
get()100     T *get() const { return raw; }
101 
102     /// Reset raw pointer - unlock any previous one and save new one without locking.
resetWithoutLocking(T * t)103     void resetWithoutLocking(T *t) {
104         unlock();
105         raw = t;
106     }
107 
resetAndLock(T * t)108     void resetAndLock(T *t) {
109         if (t != get()) {
110             resetWithoutLocking(t);
111             lock(t);
112         }
113     }
114 
115     /// Forget the raw pointer - unlock if any value was set. Become a nil pointer.
reset()116     void reset() { unlock(); }
117 
118     /// Forget the raw pointer without unlocking it. Become a nil pointer.
release()119     T *release() {
120         T *ret = raw;
121         raw = nullptr;
122         return ret;
123     }
124 
125 private:
126     /// The lock() method increments Object's reference counter.
lock(T * t)127     void lock(T *t) {
128         if (t) {
129             Locker doLock;
130             doLock(t);
131         }
132     }
133 
134     /// Become a nil pointer. Decrements any pointed-to Object's reference counter
135     /// using UnLocker which ideally destroys the object when the counter reaches zero.
unlock()136     void unlock() {
137         if (raw) {
138             UnLocker(raw);
139             raw = nullptr;
140         }
141     }
142 
143     /**
144      * Normally, no other code will have this raw pointer.
145      *
146      * However, OpenSSL does some strange and not always consistent things.
147      * OpenSSL library may keep its own internal raw pointers and manage
148      * their reference counts independently, or it may not. This varies between
149      * API functions, though it is usually documented.
150      *
151      * This means the caller code needs to be carefuly written to use the correct
152      * reset method and avoid the raw-pointer constructor unless OpenSSL function
153      * producing the pointer is clearly documented as incrementing a lock for it.
154      */
155     T *raw;
156 };
157 
158 } // namespace Security
159 
160 #endif /* SQUID_SRC_SECURITY_LOCKINGPOINTER_H */
161 
162