sched: fix and test a double deref on delete of an executing call back

sched: Avoid a double deref when AST_SCHED_DEL_UNREF is called on an
executing call-back. This is done by adding a new variable 'rescheduled'
to the struct sched which is set in ast_sched_runq and checked in
ast_sched_del_nonrunning. ast_sched_del_nonrunning is a replacement for
now deprecated ast_sched_del which returns a new possible value -2
if called on an executing call-back with rescheduled set. ast_sched_del
is modified to call ast_sched_del_nonrunning to maintain existing code.
AST_SCHED_DEL_UNREF is also updated to look for the -2 in which case it
will not throw a warning or invoke refcall.
test_sched: Add a new unit test sched_test_freebird that will check the
reference count in the resolved scenario.

ASTERISK-29698

Change-Id: Icfb16b3acbc29cf5b4cef74183f7531caaefe21d
This commit is contained in:
Mike Bradeen
2021-12-08 14:14:48 -07:00
committed by Michael Bradeen
parent 93d090147f
commit b79a571279
3 changed files with 196 additions and 8 deletions

View File

@@ -72,20 +72,22 @@ extern "C" {
/*!
* \brief schedule task to get deleted and call unref function
*
* Only calls unref function if the delete succeeded.
* Only calls the unref function if the task is actually deleted by
* ast_sched_del_nonrunning. If a failure occurs or the task is
* currently running and not rescheduled then refcall is not invoked.
*
* \sa AST_SCHED_DEL
* \since 1.6.1
*/
#define AST_SCHED_DEL_UNREF(sched, id, refcall) \
do { \
int _count = 0, _id; \
while ((_id = id) > -1 && ast_sched_del(sched, _id) && ++_count < 10) { \
int _count = 0, _id, _ret = 0; \
while ((_id = id) > -1 && (( _ret = ast_sched_del_nonrunning(sched, _id)) == -1) && ++_count < 10) { \
usleep(1); \
} \
if (_count == 10) { \
ast_log(LOG_WARNING, "Unable to cancel schedule ID %d. This is probably a bug (%s: %s, line %d).\n", _id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
} else if (_id > -1) { \
} else if (_id > -1 && _ret >-2) { \
refcall; \
id = -1; \
} \
@@ -294,9 +296,29 @@ const void *ast_sched_find_data(struct ast_sched_context *con, int id);
*
* \retval -1 on failure
* \retval 0 on success
*
* \deprecated in favor of ast_sched_del_nonrunning which checks if the event is running and rescheduled
*
*/
int ast_sched_del(struct ast_sched_context *con, int id) attribute_warn_unused_result;
/*!
* \brief Deletes a scheduled event with care against the event running
*
* Remove this event from being run. A procedure should not remove its own
* event, but return 0 instead. In most cases, you should not call this
* routine directly, but use the AST_SCHED_DEL() macro instead (especially if
* you don't intend to do something different when it returns failure).
*
* \param con scheduling context to delete item from
* \param id ID of the scheduled item to delete
*
* \retval -1 on failure
* \retval -2 event was running but was deleted because it was not rescheduled
* \retval 0 on success
*/
int ast_sched_del_nonrunning(struct ast_sched_context *con, int id) attribute_warn_unused_result;
/*!
* \brief Determines number of seconds until the next outstanding event to take place
*