[PATCH] Prevent gaschange tank icons from using garbage coords.

Dirk Hohndel dirk at hohndel.org
Wed Dec 2 21:41:52 PST 2015


> On Dec 2, 2015, at 9:20 PM, K. pestophagous Heller <pestophagous at gmail.com> wrote:
> 
> The DiveEventItem(s) need not store a pointer to memory

"need not" is odd. "should not" maybe?

> that they do not own.  This way, no matter how the path of
> execution arrives into slot recalculatePos, we never need
> fear that the DiveEventItem will dereference a garbage
> pointer to a struct event.
> 
> Fixes #968
> 
> Signed-off-by: K. Heller <pestophagous at gmail.com>
> ---
> 
> So here is one fix for trac ticket #968.  Certainly there could be
> other fixes. What follows is my rationale for doing it this way.
> 
> First thing to know is that the reason the tank icons "walk" to
> incorrect spots on the profile is that the DiveEventItem holds a
> pointer to a struct event -- even after the struct event at that
> address has been freed.
> 
> When 'internalEvent' is a pointer to freed memory,
> internalEvent->time.seconds can have all kinds of crazy values, which
> get used in DiveEventItem::recalculatePos to place the tank at weird x
> coordinates.

Some of this explanation could even migrate up to the commit message.
It's always nice to have more context there.

> (if anyone needs convincing, you can try a 'diagnostic' patch instead
> of the fix patch. one such diagnostic patch is shown:
> https://github.com/pestophagous/subsurface/commit/8625ff058fc73a2d3612655455464a9f3708615b )
> 
> As you might guess, the following function is partly implicated in the
> code path to the bug:
> 
> ProfileWidget2::plotDive

Hehe

> plotDive gets called at inopportune times. Also, there are several
> branches near the top of plotDive that cause us to exit plotDive early
> (before refreshing the gaschange events).
> 
> This function also matters:
> 
> create_dive_from_plan in subsurface-core/planner.c
> 
> because the pointers-to-struct-events get malloc'ed and freed in create_dive_from_plan.
> 
> So...
> 
> ...debate at will :)

No, your analysis seems solid.

> Could there be some other fix involving tighter coordination between
> create_dive_from_plan and ProfileWidget2::plotDive?  Maybe, but
> attempting that doesn't sound palatable to me.

No, what you are doing sounds like the right thing to do. Having a copy 
of the event seems the safest course of action. 

> Could there be a fix where we remove or adjust the ways to jump out of
> plotDive early?  Maybe, but plotDive is so huge and also so central
> that I don't want to touch it.

Wise. It's a pain to mess with it

> DiveEventItem::recalculatePos is a slot.  As many have said, one faces
> an uphill battle (a losing battle) to try to reason cleanly about
> ordering and dependency graphs and such between all the various
> signals and slots.

That's the beauty and the curse of it. It makes things presumably
easier when writing code - but then it comes back to bite you.

> Recently (here: http://lists.subsurface-divelog.org/pipermail/subsurface/2015-December/023509.html ),
> Dirk mentioned that reshuffling where the call site to plotDive goes
> might just be hiding a problem, and "we need a way to know when the
> profile is fully updated and ready to be rendered."

Yes - that's definitely something we need. Tomaz thought he had
an idea how to do that - but sadly he's very busy at work right now.

> I wholeheartedly agree. Any slot needs to be ready with its own
> internal "defenses" and sanity-checking so that it only executes its
> slot duties when it is safe.  This patch for DiveEventItem is in
> keeping with that philosophy.  DiveEventItem now won't have to blindly
> trust about the lifetime of a pointer it never properly owned. Now
> DiveEventItem can "defend itself" to never use a garbage pointer.

Good work.

> profile-widget/diveeventitem.cpp | 13 ++++++++++++-
> profile-widget/diveeventitem.h   |  1 +
> 2 files changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp
> index 0bbc842..52e4995 100644
> --- a/profile-widget/diveeventitem.cpp
> +++ b/profile-widget/diveeventitem.cpp
> @@ -19,6 +19,10 @@ DiveEventItem::DiveEventItem(QObject *parent) : DivePixmapItem(parent),
> 	setFlag(ItemIgnoresTransformations);
> }
> 
> +DiveEventItem::~DiveEventItem()
> +{
> +	free(internalEvent);
> +}
> 
> void DiveEventItem::setHorizontalAxis(DiveCartesianAxis *axis)
> {
> @@ -48,7 +52,14 @@ void DiveEventItem::setEvent(struct event *ev)
> {
> 	if (!ev)
> 		return;
> -	internalEvent = ev;
> +
> +	free(internalEvent);
> +	// logic for copying ev partially copied from copy_events.
> +	// maybe dive.c should have a 'copy_event' used here and by copy_events?

Yes, I'd like that better

> +	int size = sizeof(*ev) + strlen(ev->name) + 1;
> +	internalEvent = (struct event*) malloc(size);
> +	memcpy(internalEvent, ev, size);
> +	internalEvent->next = NULL;

that way this code would be in dive.c and we'd just call that helper.

> 	setupPixmap();
> 	setupToolTipString();
> 	recalculatePos(true);
> diff --git a/profile-widget/diveeventitem.h b/profile-widget/diveeventitem.h
> index f358fee..9d6ad5d 100644
> --- a/profile-widget/diveeventitem.h
> +++ b/profile-widget/diveeventitem.h
> @@ -11,6 +11,7 @@ class DiveEventItem : public DivePixmapItem {
> 	Q_OBJECT
> public:
> 	DiveEventItem(QObject *parent = 0);
> +	virtual ~DiveEventItem();
> 	void setEvent(struct event *ev);
> 	struct event *getEvent();
> 	void eventVisibilityChanged(const QString &eventName, bool visible);

The rest looks good to me.

Would you re-edit this and re-submit?

Thanks

/D



More information about the subsurface mailing list