]>
Commit | Line | Data |
---|---|---|
14efab78 JK |
1 | When a transport error occured on an INVITE session |
2 | the stack calls on_tsx_state_changed with new state | |
3 | PJSIP_INV_STATE_DISCONNECTED and immediately destroys | |
4 | the INVITE session. | |
5 | At the same time this INVITE session could being processed | |
6 | on another thread. This thread could use the session's | |
7 | memory pools which were already freed, so we get segfault. | |
8 | ||
9 | This patch adds a reference counter and new functions: | |
10 | pjsip_inv_add_ref and pjsip_inv_dec_ref. | |
11 | The INVITE session is destroyed only when the reference | |
12 | counter has reached zero. | |
13 | ||
14 | To avoid race condition an application should call | |
15 | pjsip_inv_add_ref/pjsip_inv_dec_ref. | |
16 | ||
17 | Index: pjsip/include/pjsip-ua/sip_inv.h | |
18 | =================================================================== | |
19 | --- a/pjsip/include/pjsip-ua/sip_inv.h (revision 5434) | |
20 | +++ b/pjsip/include/pjsip-ua/sip_inv.h (revision 5435) | |
21 | @@ -383,6 +383,11 @@ | |
22 | * Other applications that want to use these pools must understand | |
23 | * that the flip-flop pool's lifetimes are synchronized to the | |
24 | * SDP offer-answer negotiation. | |
25 | + * | |
26 | + * The lifetime of this session is controlled by the reference counter in this | |
27 | + * structure, which is manipulated by calling #pjsip_inv_add_ref and | |
28 | + * #pjsip_inv_dec_ref. When the reference counter has reached zero, then | |
29 | + * this session will be destroyed. | |
30 | */ | |
31 | struct pjsip_inv_session | |
32 | { | |
33 | @@ -412,6 +417,7 @@ | |
34 | struct pjsip_timer *timer; /**< Session Timers. */ | |
35 | pj_bool_t following_fork; /**< Internal, following | |
36 | forked media? */ | |
37 | + pj_atomic_t *ref_cnt; /**< Reference counter. */ | |
38 | }; | |
39 | ||
40 | ||
41 | @@ -631,6 +637,30 @@ | |
42 | ||
43 | ||
44 | /** | |
45 | + * Add reference counter to the INVITE session. The reference counter controls | |
46 | + * the life time of the session, ie. when the counter reaches zero, then it | |
47 | + * will be destroyed. | |
48 | + * | |
49 | + * @param inv The INVITE session. | |
50 | + * @return PJ_SUCCESS if the INVITE session reference counter | |
51 | + * was increased. | |
52 | + */ | |
53 | +PJ_DECL(pj_status_t) pjsip_inv_add_ref( pjsip_inv_session *inv ); | |
54 | + | |
55 | +/** | |
56 | + * Decrement reference counter of the INVITE session. | |
57 | + * When the session is no longer used, it will be destroyed and | |
58 | + * caller is informed with PJ_EGONE return status. | |
59 | + * | |
60 | + * @param inv The INVITE session. | |
61 | + * @return PJ_SUCCESS if the INVITE session reference counter | |
62 | + * was decreased. A status PJ_EGONE will be returned to | |
63 | + * inform that session is destroyed. | |
64 | + */ | |
65 | +PJ_DECL(pj_status_t) pjsip_inv_dec_ref( pjsip_inv_session *inv ); | |
66 | + | |
67 | + | |
68 | +/** | |
69 | * Forcefully terminate and destroy INVITE session, regardless of | |
70 | * the state of the session. Note that this function should only be used | |
71 | * when there is failure in the INVITE session creation. After the | |
72 | Index: pjsip/src/pjsip-ua/sip_inv.c | |
73 | =================================================================== | |
74 | --- a/pjsip/src/pjsip-ua/sip_inv.c (revision 5434) | |
75 | +++ b/pjsip/src/pjsip-ua/sip_inv.c (revision 5435) | |
76 | @@ -195,6 +195,65 @@ | |
77 | } | |
78 | ||
79 | /* | |
80 | + * Add reference to INVITE session. | |
81 | + */ | |
82 | +PJ_DEF(pj_status_t) pjsip_inv_add_ref( pjsip_inv_session *inv ) | |
83 | +{ | |
84 | + PJ_ASSERT_RETURN(inv && inv->ref_cnt, PJ_EINVAL); | |
85 | + | |
86 | + pj_atomic_inc(inv->ref_cnt); | |
87 | + | |
88 | + return PJ_SUCCESS; | |
89 | +} | |
90 | + | |
91 | +static void inv_session_destroy(pjsip_inv_session *inv) | |
92 | +{ | |
93 | + if (inv->last_ack) { | |
94 | + pjsip_tx_data_dec_ref(inv->last_ack); | |
95 | + inv->last_ack = NULL; | |
96 | + } | |
97 | + if (inv->invite_req) { | |
98 | + pjsip_tx_data_dec_ref(inv->invite_req); | |
99 | + inv->invite_req = NULL; | |
100 | + } | |
101 | + if (inv->pending_bye) { | |
102 | + pjsip_tx_data_dec_ref(inv->pending_bye); | |
103 | + inv->pending_bye = NULL; | |
104 | + } | |
105 | + pjsip_100rel_end_session(inv); | |
106 | + pjsip_timer_end_session(inv); | |
107 | + pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); | |
108 | + | |
109 | + /* Release the flip-flop pools */ | |
110 | + pj_pool_release(inv->pool_prov); | |
111 | + inv->pool_prov = NULL; | |
112 | + pj_pool_release(inv->pool_active); | |
113 | + inv->pool_active = NULL; | |
114 | + | |
115 | + pj_atomic_destroy(inv->ref_cnt); | |
116 | + inv->ref_cnt = NULL; | |
117 | +} | |
118 | + | |
119 | +/* | |
120 | + * Decrease INVITE session reference, destroy it when the reference count | |
121 | + * reaches zero. | |
122 | + */ | |
123 | +PJ_DEF(pj_status_t) pjsip_inv_dec_ref( pjsip_inv_session *inv ) | |
124 | +{ | |
125 | + pj_atomic_value_t ref_cnt; | |
126 | + | |
127 | + PJ_ASSERT_RETURN(inv && inv->ref_cnt, PJ_EINVAL); | |
128 | + | |
129 | + ref_cnt = pj_atomic_dec_and_get(inv->ref_cnt); | |
130 | + pj_assert( ref_cnt >= 0); | |
131 | + if (ref_cnt == 0) { | |
132 | + inv_session_destroy(inv); | |
133 | + return PJ_EGONE; | |
134 | + } | |
135 | + return PJ_SUCCESS; | |
136 | +} | |
137 | + | |
138 | +/* | |
139 | * Set session state. | |
140 | */ | |
141 | static void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, | |
142 | @@ -261,27 +320,7 @@ | |
143 | if (inv->state == PJSIP_INV_STATE_DISCONNECTED && | |
144 | prev_state != PJSIP_INV_STATE_DISCONNECTED) | |
145 | { | |
146 | - if (inv->last_ack) { | |
147 | - pjsip_tx_data_dec_ref(inv->last_ack); | |
148 | - inv->last_ack = NULL; | |
149 | - } | |
150 | - if (inv->invite_req) { | |
151 | - pjsip_tx_data_dec_ref(inv->invite_req); | |
152 | - inv->invite_req = NULL; | |
153 | - } | |
154 | - if (inv->pending_bye) { | |
155 | - pjsip_tx_data_dec_ref(inv->pending_bye); | |
156 | - inv->pending_bye = NULL; | |
157 | - } | |
158 | - pjsip_100rel_end_session(inv); | |
159 | - pjsip_timer_end_session(inv); | |
160 | - pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); | |
161 | - | |
162 | - /* Release the flip-flop pools */ | |
163 | - pj_pool_release(inv->pool_prov); | |
164 | - inv->pool_prov = NULL; | |
165 | - pj_pool_release(inv->pool_active); | |
166 | - inv->pool_active = NULL; | |
167 | + pjsip_inv_dec_ref(inv); | |
168 | } | |
169 | } | |
170 | ||
171 | @@ -838,6 +877,12 @@ | |
172 | inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session); | |
173 | pj_assert(inv != NULL); | |
174 | ||
175 | + status = pj_atomic_create(dlg->pool, 0, &inv->ref_cnt); | |
176 | + if (status != PJ_SUCCESS) { | |
177 | + pjsip_dlg_dec_lock(dlg); | |
178 | + return status; | |
179 | + } | |
180 | + | |
181 | inv->pool = dlg->pool; | |
182 | inv->role = PJSIP_ROLE_UAC; | |
183 | inv->state = PJSIP_INV_STATE_NULL; | |
184 | @@ -881,6 +926,7 @@ | |
185 | pjsip_100rel_attach(inv); | |
186 | ||
187 | /* Done */ | |
188 | + pjsip_inv_add_ref(inv); | |
189 | *p_inv = inv; | |
190 | ||
191 | pjsip_dlg_dec_lock(dlg); | |
192 | @@ -1471,6 +1517,12 @@ | |
193 | inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session); | |
194 | pj_assert(inv != NULL); | |
195 | ||
196 | + status = pj_atomic_create(dlg->pool, 0, &inv->ref_cnt); | |
197 | + if (status != PJ_SUCCESS) { | |
198 | + pjsip_dlg_dec_lock(dlg); | |
199 | + return status; | |
200 | + } | |
201 | + | |
202 | inv->pool = dlg->pool; | |
203 | inv->role = PJSIP_ROLE_UAS; | |
204 | inv->state = PJSIP_INV_STATE_NULL; | |
205 | @@ -1540,6 +1592,7 @@ | |
206 | } | |
207 | ||
208 | /* Done */ | |
209 | + pjsip_inv_add_ref(inv); | |
210 | pjsip_dlg_dec_lock(dlg); | |
211 | *p_inv = inv; | |
212 |