Killing the module from the own module code

15 views
Skip to first unread message

mrs...@gmail.com

unread,
Jul 7, 2006, 5:51:57 PM7/7/06
to
I'v got a question about killing the module from it's own module code.
I''ve seen that some modules use OS_Module 4 to kill themselfs. For
example the module has a SWI which kill the module (like the ABImodule)
or a outcome of a service call kills the module. The SWI OS_Module
which kills the module is then 1 instruction away from the ending
instruction (like MOV PC,R14 or LDMFD R13,{....PC}) which ends the SWI
or ends the service call routine.

I think this coding is not right because after the module is killed the
area in which the module was stored is not anymore allocated. This area
can have been overwritten which can corrupt the ending instruction and
then things get horrible wrong. Ok that the ending instruction becomes
corrupted is not likely but can happen. I found one case hat this
really happens.

My question is: is there a safe way of killing the module from it's own
module code. I know one way which works. Start a wimptask in the
module and kill it with OS_ExitAndDie. Does anybody know another way to
kill to own module safely?

Dick Tanis.

Ben Avison

unread,
Jul 8, 2006, 9:53:15 AM7/8/06
to
> My question is: is there a safe way of killing the module from it's own
> module code. I know one way which works. Start a wimptask in the
> module and kill it with OS_ExitAndDie. Does anybody know another way to
> kill to own module safely?

The obvious solution is to turn off IRQs before calling OS_Module, and
then exiting the module with LDM sp!, {... pc}^ to atomically return and
re-enable IRQs. You will need to check if you're running in 32-bit mode
and set up SPSR_svc in preparation if so. This should be safe as long as
the implementation of OS_Module doesn't claim a block of RMA or reenable
IRQs before returning, which it doesn't do and is unlikely to do in the
future.

Ben

Justin Fletcher

unread,
Jul 8, 2006, 10:36:24 AM7/8/06
to

The atomic return is redundant; once you've returned to that code and the
block has been deallocated it doesn't really matter whether it's 1
instruction or any other number that are executed on return from the call.
The memory you were executing from does not belong to you and may have
been replaced with anything. You have NO guarentees from the OS or from
anyone else that the memory that you were executing in will remain
present, or that it won't have been cleared by the action of your killing
the module.

There are a few scenarios where that recommendation may fail, not least of
which is the obvious case of the OS reducing the size of the RMA because
the module was the last block allocated (no guarentees that it will or
won't do that), or of the memory mapping being removed because it can be
freed to the pool (think of a sparsely mapped RMA). Then of course there
are any of the operations which are triggered off the module's death -
Service_ModuleStatus or the equivilent on other systems.

Furthermore, I see very little need to ever kill a module from within
itself. The only cases I have ever found that were useful were those which
provide multiple instantiation, in which case you never terminate the base
instance, only the subsequent instantiations, in which case the code you
are executing from is, by definition, guarenteed to remain present.

Other solutions that might be suggested and will fail include... dynamic
code on the SVC stack (will fail if the stack is in non-executable space
which it may be on some systems), using scratch space as a trampoline (any
operation within OS_Module may use scratch space; there may be error look
ups, file operations, variable expansions or any of the other fun things
that use it), using other reserved zero page addresses as a trampoline (it
ain't yours and you shouldn't be playing with it - under some system
you're going to screw up)... yeah, I'm out of ideas about how you might
abuse the system like that.

My personal favourite, if you desperately need to do this and which is
absolutely safe under all circumstances, would be to use
SWI TaskManager_StartTask R0="RMKill Bingle", which will defer the module
death until the next Wimp poll which may not be desireable but if the
module is truely in a state it cannot function in it should merely report
errors in those calls.

--
Gerph <http://gerph.org/>
... It's really me, I really do, remember every moment magnified.

mrs...@gmail.com

unread,
Jul 8, 2006, 3:38:34 PM7/8/06
to
Ben Avison wrote:

> The obvious solution is to turn off IRQs before calling OS_Module, and
> then exiting the module with LDM sp!, {... pc}^ to atomically return and
> re-enable IRQs. You will need to check if you're running in 32-bit mode
> and set up SPSR_svc in preparation if so. This should be safe as long as
> the implementation of OS_Module doesn't claim a block of RMA or reenable
> IRQs before returning, which it doesn't do and is unlikely to do in the
> future.

What Justin already said, even that is not safe, you're right about the
OS_Module SWI (that's quite safe) but after the module is killed
(finalisation completed) service call Service_ModulePostInit is
broadcasted. A module could act upon it and claim some RMA in the
process which corrupts my module.

Dick Tanis.

mrs...@gmail.com

unread,
Jul 8, 2006, 3:58:05 PM7/8/06
to
Justin Fletcher wrote:

> Furthermore, I see very little need to ever kill a module from within
> itself.

But I do. Especially modules which give another desktop welcome message
and not needed anymore after that. You could use !Boot to kill the
module but it's nicer to do it yourself.

> Other solutions that might be suggested and will fail include... dynamic
> code on the SVC stack (will fail if the stack is in non-executable space
> which it may be on some systems), using scratch space as a trampoline (any
> operation within OS_Module may use scratch space; there may be error look
> ups, file operations, variable expansions or any of the other fun things
> that use it), using other reserved zero page addresses as a trampoline (it
> ain't yours and you shouldn't be playing with it - under some system
> you're going to screw up)... yeah, I'm out of ideas about how you might
> abuse the system like that.

That's why I'm looking for an absolute safe method which is doesn't use
nasty code.

> My personal favourite, if you desperately need to do this and which is
> absolutely safe under all circumstances, would be to use
> SWI TaskManager_StartTask R0="RMKill Bingle", which will defer the module
> death until the next Wimp poll which may not be desireable but if the
> module is truely in a state it cannot function in it should merely report
> errors in those calls.

I don't know TaskManager_StartTask, I think you mean Wimp_StartTask. Ok
that could work but you have to execute it from a wimptask, be in user
mode and you need an empty SVC stack according to the PRM. But if you
already need a task for this SWI, it's easier to use OS_ExitAndDie.

Dick Tanis.

Justin Fletcher

unread,
Jul 8, 2006, 4:35:05 PM7/8/06
to
On Sat, 8 Jul 2006, mrs...@gmail.com wrote:

> Justin Fletcher wrote:
>
>> Furthermore, I see very little need to ever kill a module from within
>> itself.
>
> But I do. Especially modules which give another desktop welcome message
> and not needed anymore after that. You could use !Boot to kill the
> module but it's nicer to do it yourself.

The welcome message code is all of 1k. If you're writing it in C you might
have 8 or 9k. It's hardly complex, and leaving it resident does no real
harm to the system. I don't see why you don't just hang around afterward.
The service call you are providing will have been hashed into the system
table and will therefore not impact general system performance (this will
not be the case on legacy OS versions, but the impact is negligable and we
don't care about them anyhow). As you provide no SWIs and no *Commands
(presumably), there will be no impact on the SWI dispatch or the command
lookup (with the exception of directed commands, but they're so rarely
used and in any case *Commands are relatively high level so the impact is
negligable). The extra module should not really be that significant.

Unless, of course, you're building a large image into the module itself,
but you wouldn't do that, would you ? It'd just be unnecessarily wasting
RMA...

However, with this information in mind, I have a solution which may
suffice for your purposes if I've remembered the API ordering correctly.

Display your banner on Service_DesktopWelcome.
Watch for the subsequent Service_StartWimp.
Return "*RMKill Bingle" as the command to start your module in R0 and
claim the service.

Done. Because you've returned from the service with a message to kill
yourself, you won't see the subsequent services as the Service_StartWimp
cycle runs. The ordering of DesktopWelcome and StartWimp is implicitly
guarenteed by the Desktop module because the purpose of the banner is to
be displayed whilst the tasks are starting up. You must, as with all
modules, exit cleanly. Failure to do so in this case will be more
significant as it will prevent the desktop from starting correctly.

Obviously killing your module in this way will prevent subsequent desktop
re-entry from displaying any custom banner and is on your own head as
that's what you're explicitly doing.

>> Other solutions that might be suggested and will fail include... dynamic
>> code on the SVC stack (will fail if the stack is in non-executable space
>> which it may be on some systems), using scratch space as a trampoline (any
>> operation within OS_Module may use scratch space; there may be error look
>> ups, file operations, variable expansions or any of the other fun things
>> that use it), using other reserved zero page addresses as a trampoline (it
>> ain't yours and you shouldn't be playing with it - under some system
>> you're going to screw up)... yeah, I'm out of ideas about how you might
>> abuse the system like that.
>
> That's why I'm looking for an absolute safe method which is doesn't use
> nasty code.

As I said; those are other suggestions that won't work.

>> My personal favourite, if you desperately need to do this and which is
>> absolutely safe under all circumstances, would be to use
>> SWI TaskManager_StartTask R0="RMKill Bingle", which will defer the module
>> death until the next Wimp poll which may not be desireable but if the
>> module is truely in a state it cannot function in it should merely report
>> errors in those calls.
>
> I don't know TaskManager_StartTask, I think you mean Wimp_StartTask. Ok

Trust me. In this case when I say TaskManager_StartTask, I mean
TaskManager_StartTask.

> that could work but you have to execute it from a wimptask, be in user
> mode and you need an empty SVC stack according to the PRM. But if you
> already need a task for this SWI, it's easier to use OS_ExitAndDie.

Wimp_StartTask is not safe as you cannot use it from SVC mode (as you
said) without a special truss and even in those circumstances it is unsafe
unless you know that the enviroment within which you are launching is safe
to do so.

--
Gerph <http://gerph.org/>
... Are you happy where you're sleeping ? Does he keep you safe and warm ?

Ben Avison

unread,
Jul 8, 2006, 5:07:02 PM7/8/06
to
On Sat, 08 Jul 2006, Justin Fletcher wrote:
> On Sat, 8 Jul 2006, Ben Avison wrote:
>> This should be safe as long as
>> the implementation of OS_Module doesn't claim a block of RMA or reenable
>> IRQs before returning, which it doesn't do and is unlikely to do in the
>> future.
>
> The atomic return is redundant; once you've returned to that code and
> the block has been deallocated it doesn't really matter whether it's 1
> instruction or any other number that are executed on return from the
> call. The memory you were executing from does not belong to you and may
> have been replaced with anything. You have NO guarentees from the OS or
> from anyone else that the memory that you were executing in will remain
> present, or that it won't have been cleared by the action of your
> killing the module.

This is quite correct, but as you point out, the alternatives are nasty
too. In practice I'd expect the OS_Heap block free to always leave the
memory block itself intact (anything else would kill system performance)
and I *did* qualify the bit about OS_Module not re-using the RMA as being
"unlikely" not impossible. In any case, I had forgotten about the service
calls which have been added in the last few years (I must be getting
rusty), and these will unfortunately enable IRQs, so that falls into my
other caveat. Now that I check, OS_Module (as a whole, unhelpfully) is
documented such that the IRQ status is undefined, so it's within its
rights to do so.

I don't think the TaskManager_StartTask method is necessarily a good
solution either, as it may well be necessary to kill the module before
the next Wimp_Poll, and who's to say you're even executing in the Desktop?

I think the least-worst solution is probably the self-destructing code on
the SVC stack - that was my first knee-jerk thought as it happens, but
I thought it might be more complicated than necessary, so neglected to
mention it. Your objection to that is on the grounds that the SVC stack
might be marked as non-executable in future, but while I can appreciate
the security benefits this might bring, executing code on the SVC stack
has been an established technique for many years, and is often the only
way to achieve a particular effect, this just being one example.

> Furthermore, I see very little need to ever kill a module from within
> itself.

Here's an example: in the UpCall_NewApplication handler of a module
wrapped application. The application needs to remove itself and its
supporting module immediately, and you can't use OS_ExitAndDie because
you need to return via the handler return address rather than the exit
handler. I think the examples I've seen use the IRQs off trick I
described, but as we have established this isn't bulletproof in recent
OSes, and they should probably be changed to use the SVC stack instead...

Ben

Justin Fletcher

unread,
Jul 8, 2006, 10:54:13 PM7/8/06
to

This is, of course, true. However, as I said about the necessity to kill
the module, there is no reason why the module cannot just reject all calls
to itself (Silently or noisily). Its continued existence is far less
destructive than to employ unsafe operations such as code on the SVC
stack, or continued execution in freed space. As for not being in the
desktop; I agree that they might not be.

> I think the least-worst solution is probably the self-destructing code on
> the SVC stack - that was my first knee-jerk thought as it happens, but
> I thought it might be more complicated than necessary, so neglected to
> mention it. Your objection to that is on the grounds that the SVC stack
> might be marked as non-executable in future, but while I can appreciate
> the security benefits this might bring, executing code on the SVC stack
> has been an established technique for many years, and is often the only
> way to achieve a particular effect, this just being one example.

The stack is not for code. It's as simple as that really. If you want to
put things on there to get around deficiencies in other parts of the
system then you are tackling a symptom of a problem rather than the
problem as a whole. If you put code on the SVC stack in the knowledge that
it isn't for code then you can expect that things will break in the future
if it is no longer capable of executing code. I would have thought that
such behaviour was a thing of the dim and distant past - it was, as I
recall - strongly discouraged (don't have the docs in front of me to
say the exact wording) from being used when the StrongARM came in and
developers should have kept well away from introducing new code that did
so really.

The need is for a mechanism to terminate the module which is safe. With
the exception of potentially not actually happening (!), the
TaskManager_StartTask mechanism meets that criteria. The worst that can
happen is that the module can be called at a service, vector, command or
swi entry point, all of which can dealt with safely. The alternatives of
returning to the no-longer owned code (prefetch abort, wrong code
executed, undefined instruction), or executing code on the stack (prefetch
abort, undefined behaviour) are clearly never acceptable. Leaving things
hanging around for an indeterminate period isn't desireable, but it's
acceptable, whereas the potentially fatal results of your alternatives are
not.

My view of what's acceptable, yours, and the original poster's may be
wildly different, though :-) Fortunately, the wimp start mechanism I
suggested in a previous posting offers far neater solution (again, solely
in my opinion, and presuming that my understanding of the desktop entry
sequence is correct) to their actual use-case.

>> Furthermore, I see very little need to ever kill a module from within
>> itself.
>
> Here's an example: in the UpCall_NewApplication handler of a module
> wrapped application. The application needs to remove itself and its
> supporting module immediately, and you can't use OS_ExitAndDie because
> you need to return via the handler return address rather than the exit
> handler. I think the examples I've seen use the IRQs off trick I
> described, but as we have established this isn't bulletproof in recent
> OSes, and they should probably be changed to use the SVC stack instead...

You don't want to kill the module in those circumstances though - at least
I don't think you do under any sane circumstances. If you are running as a
module task it is for the express purpose of remaining resident and
monitoring other, non-task related functions. Personally I think that
module tasks are better handled as a two-component process, one of which
is the task and one of which is the monitor / support functions;
experience has shown that it is very easy to become confused about the
which hat you have on in any part of the code, even in a well designed
module task. But I digress...

If you are such a module task then you are doing so because you want to
remain resident to continue providing those support functions and so that
you can be restarted. If you merely kill the module off at the first
instance of a replacement task then you might as well just be an
application - you would be /better/ as an application because you are not
wasting RMA on a user-mode component which doesn't need to be there, and
you are not wasting RMA on what, presumably is your user mode stack
(invariably module-tasks use RMA-based stacks and relinquish their
application space which is, in my opinion, a false economy).

As an aside, and off the idea of your atomic instruction, I was trying to
work out a sensible way to use that mechanism to both return from the call
AND to free the block in a single operation. However the only way I could
see that you could do that sort of thing would be to use a SWP instruction
(as this is the only instruction which can both read and write atomically)
to write a value to the heap which updates the size of the block to either
gift the block you were executing to the previous block, or to extend a
free heap block. But the only way that would be able to be useful would be
to load the PC at the same time, which is clearly unuseful and would mean
that whilst the PC was held in the size field the heap was corrupt. Not a
solution. However it does lead you to the guarenteed solution of 'turn off
IRQs and manipulate the entire heap yourself' and then return with the PSR
restore as you suggested. Whilst that would fulfill all the objectives -
being safe, using only defined operations (as the Heap is of a defined
format [hmm. the heap is, but is OS_Module defined to be managed in terms
of OS_Heap? can't remember]), and returning to the caller properly, I do
think it's far more faff than is justified for what is an incredibly rare
operation.

--
Gerph <http://gerph.org/>
... We all want to tell her, tell her that we love her.

mrs...@gmail.com

unread,
Jul 9, 2006, 9:27:21 AM7/9/06
to
Justin Fletcher wrote:

> The extra module should not really be that significant.

You're right but on systems with less memory like ROS 3.1 etc it's
important to save every kilobyte as possible. When I write something,
mosttimes it must run from ROS 3.1 to ROS 5. So killing my module is
important to save memory. In my opinion modules should always kill
themselves when they are not needed anymore. Especially when modules
are writen for as an extend for applications and not for common things.
Like Eurekautils for Eureka, Impression mods like Spell and hyphenator
for Impression. Timermod for KinoAmp. It's too bad that Acorn didn't
make a procedure for easy taskregistrating at a module and kill it
automaticly when all clients are stopped. I add this on my system by
runnig a seperate task in the background which kills modules when
application don't need anymore.

> Unless, of course, you're building a large image into the module itself,
> but you wouldn't do that, would you ? It'd just be unnecessarily wasting
> RMA...

Yes it is. I only use module space to store some status words or a
small stringbuffer. In this way my module doesn't fragment the RMA very
much.

> Display your banner on Service_DesktopWelcome.
> Watch for the subsequent Service_StartWimp.
> Return "*RMKill Bingle" as the command to start your module in R0 and
> claim the service.

Tried it, works perfectly. Thanks for the tip.

> Obviously killing your module in this way will prevent subsequent desktop
> re-entry from displaying any custom banner and is on your own head as
> that's what you're explicitly doing.

Yes I want that because returning to the desktop isn't an option
anymore on modern OS and on old OS it never worked right. Mosttimes it
goes wrong because there is something in the desktopfile whih gives
problems. On my A5000 I even aliased shutdown to set Alias$Desktop
which reset my machine when you click on 'Restart' in the shutdown
window.

> Trust me. In this case when I say TaskManager_StartTask, I mean
> TaskManager_StartTask.

Yes I checked the module and it has the SWI. Only one problem, it's
only supported on ROS 4 (don't know about ROS 5) and I think it's for
internal use only. It's not even mentioned in the PRM of ROS 4.

Dick.
--
Advice of a RUNner: Beware, Snatchers are everywhere.

James Peacock

unread,
Jul 9, 2006, 9:54:25 AM7/9/06
to
mrs...@gmail.com wrote:
> Justin Fletcher wrote:
>> Trust me. In this case when I say TaskManager_StartTask, I mean
>> TaskManager_StartTask.
>
> Yes I checked the module and it has the SWI. Only one problem, it's
> only supported on ROS 4 (don't know about ROS 5)

It appears to be available on RISC OS 5 at least:

Module info
===========

Title : TaskManager
Help : Task Manager 1.28 (16 Feb 2006)

SWIs :
&42680 TaskManager_TaskNameFromHandle
&42681 TaskManager_EnumerateTasks
&42682 TaskManager_Shutdown
&42683 TaskManager_StartTask

James

ne...@rtrussell.co.uk

unread,
Jul 12, 2006, 10:01:09 AM7/12/06
to
Justin Fletcher wrote:
> The stack is not for code. It's as simple as that really.

On what grounds do you make such a dogmatic statement? Sure, executing
code on the stack (or in any other 'data' area) has a bad reputation
because of its use by viruses and other malware, but that doesn't mean
it is inherently wrong. It makes rather a nonsense of the von Neumann
architecture if you say that code mustn't be executed in 'data' memory!

Richard.
http://www.rtrussell.co.uk/
To reply by email change 'news' to my forename.

Ste (news)

unread,
Jul 12, 2006, 11:03:26 AM7/12/06
to
In article <1152712869.1...@m73g2000cwd.googlegroups.com>,
<ne...@rtrussell.co.uk> wrote:
>
> [...] It makes rather a nonsense of the von Neumann architecture if you

> say that code mustn't be executed in 'data' memory!

All modern ARMs (with a cache) are based upon the Harvard architecture,
rather than the von Neumann - hence separate data and instruction caches.

(Note: ARMs are often described as Harvard-like rather than Harvard in the
strictest sense, which never allows data memory to be executed because it is
physically separated from the execution unit).

This is what makes self-modifying code so expensive on ARM. Take a look at
the PRM entry for OS_SynchroniseCodeAreas.

Steve

--
Steve Revill @ Home
Note: All opinions expressed herein are my own.

Justin Fletcher

unread,
Jul 12, 2006, 12:56:02 PM7/12/06
to
On Wed, 12 Jul 2006, ne...@rtrussell.co.uk wrote:

> Justin Fletcher wrote:
>> The stack is not for code. It's as simple as that really.
>
> On what grounds do you make such a dogmatic statement? Sure, executing
> code on the stack (or in any other 'data' area) has a bad reputation
> because of its use by viruses and other malware, but that doesn't mean
> it is inherently wrong. It makes rather a nonsense of the von Neumann
> architecture if you say that code mustn't be executed in 'data' memory!

Grounds:

Experience, practicality, a solid understanding of RISC OS, and
application of that to the case at hand.


Justification:

As Steve Revill has already stated, the use of dynamic code is very costly
within many ARM systems. It's not necessary to go in to that further as
it's both obvious and been said.

Your own statement of use by malicious code is one example which can be
used to give a very good reason why such things should not be done.

However, it is the failure cases under 'expected' circumstances which
concern me more than that [1].

The SVC stack, which we are considering, is not a region of memory which
is expected to be used for the execution of code, and thus is not
protected by clients which expect to modify it with Instructrion Memory
Barrier (aka 'Synchronise Code Areas' within the RISC OS API). The effect
of this is that any client which 'swaps' the SVC stack (or otherwise
performs operations on it) will not use the costly IMB operations in order
to ensure that the instruction and data cache are coherent should
execution return to those points. The clients which perform this
operation may be limited, but do exist and a particular example
(TaskWindow) is not 'rare'.

TaskWindow performs this action (see the PRMs for the behaviour of UpCall
6 and TaskWindow in general). Any client which performs a similar
operation to that of TaskWindow in regard to a process swap may do the
same. I have written code which does so for not-yet-released components.
One active individual on this newsgroups will be using a similar operation
within their implementation of an OS extension [2]. UnixLib ought to be
doing the same as part of its implementation [3]. And I know of other
companies who have used the technique in even more complex environments
in order to perform specific tasks required by customers. [4]


Example failure case:

If you require a specific example of such a failure case, we can use
TaskWindow as an example [5], although any of the above can be used under
particular circumstances. For our example we'll say that the killing of
the module should be achieved from within a SWI call (the original
poster's case of wishing to kill the task within the
Service_DesktopStartupBanner would not, under current, unaugmented,
versions of the Operating System provoke a problem unless the user or
another client manually issued the service within a TaskWindow)...

* SWI call issued to kill the module.
* SWI handler for module called
* Place small section of code on the stack to handle return to caller.
* Issue SWI OS_Module to kill our module
* Module finalisation code called
(insert some code here)
* Module unlinked from module chain, SWI hashing chain, service
hashing chain.
* Notifications of instance death to other modules by Service_ModuleStatus.
* Other clients deal with module death (CLIV, in particular will
reorganise the module in its command hash chains), any of which may
(insert some code here)
* Notifications of module death (if this was the last instance of the
module) with Service_ModuleStatus (CLIV will unlink the module at
this point), any of which may (insert some code here).
* IRQs are enabled throughout this process, so at any time any ticker
event (or other IRQ) may be triggered and may (insert some code here).
Similarly, code which does not directly trigger the failure code
may claim or release memory (including the Kernel, which may do so
to free the ticker blocks back to the system heap), which will cause
services to be dispatched to all modules which may (insert some code
here).
* Return from SWI call to prepared SVC stack return instructions, which
may not have been synchronised because the stack may have been
switched in and out multiple times by the '(insert some code here)'
operations. Failure will only occur if the ICache has become populated
by some other code within the region in question - so a single client
performing this task within the SVC may appear to function correctly,
but were there to be multiple users of the SVC stack this would be
more likely. Obviously this is based on the present ICache users;
we presume that there is no ICache lockdown imposed at some point
during the operation, or any more esoteric operations that I can't
even think of :-)

The (insert some code here) section is any operation which may cause a
task switch by the TaskWindow (or other client performing a similar
task). Operations which fall in to this category for TaskWindow
include (but are not limited to) :

* Character read operations (RdChV aka OS_ReadC, ByteV 129 aka OS_Byte)
* Character write operations (WRCHV aka OS_Write* or any operation
which might call those routines)
* Certain graphics operations (OS_Plot in particular, as this degrades
to WRCHV)
* Sleep operations (UpCallV 6, and any operation which may trigger it,
including socket operations and PipeFS)

Under present, unaugmented, versions of TaskWindow the preemption timer
will not be triggered unless the SVC stack were empty (this uses a
callback handler) and as such would not occur in the above sequence. If
the module had been a module task issuing the SWI OS_Module kill module
operation from USR mode then the preemption timer would have an
opportunity to be fired on return from that SWI and before the return
instructions which were placed on the stack were returned to.

In addition to the above direct trigger operations, other clients may be
triggered which use callbacks (consider a filing system operation on a
network filing system which must trigger callbacks - any callback might
perform any of the above operations to cause a SVC stack switch).

This is a little wordy in order to cover as many cases as I can in one
go. If you want an explicit example that will cause an SVC stack switch in
the above case, "OS_UpCall 6,0" ('sleep, but return quickly') would do, or
outputting a stream of characters (to guarentee the switch you would need
to use a number of characters larger than the TaskWindow buffer; it's
never been guarenteed, but you might expect that 256 characters would
trigger the switch because this is larger than the maximum size of the
Wimp message buffer - this is NOT however, guarenteed and is not readable
by any current mechanism), or writing data to a PipeFS file large enough
to cause it to block (OPENOUT("Pipe:$.Blah"):GBPB write 256 bytes:CLOSE;
although it's never been guarenteed that PipeFS would use 256 as its
blocking size within TaskWindows - this is only an indirect use of
OS_UpCall 6, however).

It might be reasonably expected that files be closed on module death and
any file which was user specified might be a PipeFS file. Similarly, said
files may be on a network filing system. And let's just just muddy the
waters futher by saying that the user might have issued *OPT 1,2 to get
information on all file accesses - which is written to the character
output as normal (consult the BBC users guide that you got with your
beeb... or the PRMs :-) )

[1] Which isn't to say that malicious code exploits do not concern me, but
they are secondary when completely valid code can be caused to fail by
ill-advised operations such as this.

[2] Can't say more than that.

[3] I have not investigated this directly, but it can be inferred from
what it needs to do <smile>.

[4] Yes, I know I'm being vague about specifics here; I don't remember
whether the individuals and companies involved ever released or intend to
release anything, but the knowledge that they exist is sufficient.

[5] We will leave the argument aside as to whether TaskWindow /should/ be
performing an IMB on the SVC stack. However, the general argument against
it is 'code shouldn't be on the SVC stack, it never has in the past so
code that /was/ on the SVC stack would fail anyhow, and it will cause a
penalty in switching tasks'.

--
Gerph <http://gerph.org/>
... I wanna show you something; just put your trust in me.

Dave Higton

unread,
Jul 12, 2006, 5:21:36 PM7/12/06
to
In message <Pine.LNX.4.63.06...@buttercup.gerph.org>
Justin Fletcher <ge...@gerph.org> wrote:

[some very interesting stuff]

Justin, thank you. It's always a pleasure to read your detailed
explanations of what goes on, or what can happen. Please come back
to visit us in csap more often!

BTW what are you doing these days?

Dave

Ben Avison

unread,
Jul 12, 2006, 6:22:03 PM7/12/06
to
On Wed, 12 Jul 2006, Justin Fletcher wrote:
> any client which 'swaps' the SVC stack (or otherwise performs operations
> on it) will not use the costly IMB operations in order
> to ensure that the instruction and data cache are coherent should
> execution return to those points. The clients which perform this
> operation may be limited, but do exist and a particular example
> (TaskWindow) is not 'rare'.

As you describe further on, there are two situations in which TaskWindow
can swap out the SVC stack. One is when the timeslice is up; the way that
this is deferred by setting the callback flag is mandated because user mode
clients of TaskWindow expect to be able to call non-reentrant SWIs, just
like any other user mode task, so the current situation where the SVC stack
only contains data put there by TaskWindow itself must continue.

The other case is when UpCall 6 is issued, either directly, or indirectly
because you've made a blocking (typically I/O) call. When running under
TaskWindow, the OS's character IO routines are intercepted and replaced
by code that effectively does UpCall 6, so I'll lump that in with other
blocking I/O from filing systems etc. If I may play devil's advocate, this
would only cause problems for code on the SVC stack if the following
happened:

1) code is constructed on the SVC stack (making the SVC stack non-empty)
2) the instruction cache is synchronised
3) a blocking call is made that issues UpCall 6; the task is switched out
4) the task is switched back in and UpCallV returns
5) the code on the SVC stack is executed

Now this is unlikely, and easily remedied by doing another instruction
cache sychronise after step 4. More importantly, your failure cases rely
upon I/O calls being made either from within a service call handler, or
even worse from within an IRQ handler. I'm pretty certain that this has
never been safe from operating system extensions like service calls, and
it definitely isn't from IRQ handlers (the character I/O SWIs are
documented as non-reentrant for example).

These sorts of rules are what makes RISC OS much easier to work with at
a low level than other OSes. The alternative is to sprinkle the code
liberally with mutexes and semaphores, which can lead to increased
task switching (at which ARMs aren't particularly efficient) and hours
of fun trying to sort out deadlocks.

> If the module had been a module task issuing the SWI OS_Module kill
> module operation from USR mode then the preemption timer would have an
> opportunity to be fired on return from that SWI and before the return
> instructions which were placed on the stack were returned to.

That's a red herring - the USR mode case is already adequately catered
for by OS_ExitAndDie.

I suppose the least controversial solution might be a kind of
OS_CallASWIThenLongJmp SWI, which specifies a return context [1] to be
used on exit instead of returning to the caller. However, it does seem
quite an obscure SWI at first glance. But maybe you could make the same
argument against OS_ExitAndDie?

Ben

1: a register dump block pointed to by R10 perhaps, but then where do
you put that? On the SVC stack? ;-)

Ste (news)

unread,
Jul 12, 2006, 6:55:55 PM7/12/06
to
In article <op.tclkd...@balaptop.lan>,

Ben Avison <usenet...@avison.me.uk> wrote:
> On Wed, 12 Jul 2006, Justin Fletcher wrote:
> > If the module had been a module task issuing the SWI OS_Module kill
> > module operation from USR mode then the preemption timer would have an
> > opportunity to be fired on return from that SWI and before the return
> > instructions which were placed on the stack were returned to.
>
> That's a red herring - the USR mode case is already adequately catered
> for by OS_ExitAndDie.

This may be stating the obvious here, but if you're a module task then there
is no problem killing yourself - you just call OS_ExitAndDie (or set some
flag if your in the module part of yourself rather than the task part which
tells the task part to kill itself).

If you're simply a module with no task part, then give yourself a task part
which calls OS_ExitAndDie. If you want to die, invoke your start code.

Or have I missed something here?

Ben Avison

unread,
Jul 13, 2006, 7:20:25 PM7/13/06
to
Ste (news) wrote:
> If you're simply a module with no task part, then give yourself a task
> part which calls OS_ExitAndDie. If you want to die, invoke your start
> code.
>
> Or have I missed something here?

Well, it has the side-effect that it replaces the current application and
then exits to the parent environment:


Supervisor

*BASIC
ARM BBC BASIC V version 1.16 (C) Acorn 1989

Starting with 12345678 bytes free

> SYS "MyModule_FallOnSword"
*


Probably not what you intended.

Ben

Peter Naulls

unread,
Jul 13, 2006, 10:27:34 PM7/13/06
to
In message <d0fd75454e...@dsl.pipex.com>
Dave Higton <daveh...@dsl.pipex.com> wrote:

> In message <Pine.LNX.4.63.06...@buttercup.gerph.org>
> Justin Fletcher <ge...@gerph.org> wrote:
>
> [some very interesting stuff]
>
> Justin, thank you. It's always a pleasure to read your detailed
> explanations of what goes on, or what can happen. Please come back
> to visit us in csap more often!

I don't have any problem with Justin's comments of course (although I
did disagree with something recently), and although I don't have any
trouble understanding it, both the length and lack of immediate
relevance to me meant my eyes quickly glazed over.

True, it's there in Google to be searched in future, and sometimes
that's a valuable resource, but I wonder if someone wanted to take the
initiative and start a collection of "collected wisdoms" - if you wanted
to start with stuff Justin's said, then fine. The place for this would
be on the riscos.info Wiki. It'd be similar in nature to Acorn's
application notes or the document collection that was kept by Robin
Watts, but with the important difference of being collaboratively edited
and kept up to date.

--
Peter Naulls - pe...@chocky.org | http://www.chocky.org/
----------------------------------------------------------------------------
RISC OS Community Wiki - add your own content | http://www.riscos.info/

Theo Markettos

unread,
Jul 14, 2006, 6:11:09 AM7/14/06
to
Peter Naulls <pe...@chocky.org> wrote:
> I don't have any problem with Justin's comments of course (although I
> did disagree with something recently), and although I don't have any
> trouble understanding it, both the length and lack of immediate
> relevance to me meant my eyes quickly glazed over.

Justin's emailed me comments occasionally on postings I've made, and it's
been very interesting to read them, particularly the in-depth background.

> True, it's there in Google to be searched in future, and sometimes
> that's a valuable resource, but I wonder if someone wanted to take the
> initiative and start a collection of "collected wisdoms" - if you wanted
> to start with stuff Justin's said, then fine. The place for this would
> be on the riscos.info Wiki. It'd be similar in nature to Acorn's
> application notes or the document collection that was kept by Robin
> Watts, but with the important difference of being collaboratively edited
> and kept up to date.

I maintain a collection of documents similar to Robin Watts at:
http://www.markettos.org.uk/riscos/techdocs.html

Possibly a bit better categorisation would help, but for me it's very easy
to add to it: save the news article as a file in my public-html directory
and add a link to the index page. Takes about a minute. One of the key
points is that my site is a collection of documents: some of the files on my
webserver have 1989 modification dates because they've been intact since
1989 - the structure and metadata are preserved.

The wiki is an interesting alternative and one I'd support, but it's rather
more work: have to create a new page, cut and paste the text in, and then
fiddle with the formatting. However I completely agree it's a better place
for things - particularly if there is more than one person maintaining it.
I might think about putting new stuff on the wiki. As is usually the case
with free projects I might start with things that interest me - which tend
to be hardware related.

Theo

d...@nospampod51.demon.co.uk

unread,
Jul 14, 2006, 9:37:56 PM7/14/06
to
In message <op.tcnhr...@balaptop.lan>
"Ben Avison" <usenet...@avison.me.uk> wrote:

You could set up LR to be the return address, then branch to the code to
perform the module die, but that involves lots of SWI table hackery which
I'm sure people will object to. (i.e. tail end call optimise it).

--
Dan Ellis

Reply all
Reply to author
Forward
0 new messages