diff --git a/os/include/os/attached_dataspace.h b/os/include/os/attached_dataspace.h
new file mode 100644
index 0000000000..87b104491d
--- /dev/null
+++ b/os/include/os/attached_dataspace.h
@@ -0,0 +1,83 @@
+/*
+ * \brief  Dataspace utility
+ * \author Norman Feske
+ * \date   2014-01-10
+ */
+
+/*
+ * Copyright (C) 2014 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#ifndef _INCLUDE__OS__ATTACHED_DATASPACE_H_
+#define _INCLUDE__OS__ATTACHED_DATASPACE_H_
+
+#include <dataspace/client.h>
+#include <base/env.h>
+
+namespace Genode { class Attached_dataspace; }
+
+
+class Genode::Attached_dataspace : Noncopyable
+{
+	public:
+
+		/**
+		 * Exception type
+		 */
+		class Invalid_dataspace { };
+
+	private:
+
+		Dataspace_capability _ds;
+
+		size_t const _size = { Dataspace_client(_ds).size() };
+
+		void * const _local_addr = { env()->rm_session()->attach(_ds) };
+
+		Dataspace_capability _check(Dataspace_capability ds)
+		{
+			if (ds.valid())
+				return ds;
+
+			throw Invalid_dataspace();
+		}
+
+	public:
+
+		/**
+		 * Constructor
+		 *
+		 * \throw Rm_session::Attach_failed
+		 * \throw Invalid_dataspace
+		 */
+		Attached_dataspace(Dataspace_capability ds) : _ds(_check(ds)) { }
+
+		/**
+		 * Destructor
+		 */
+		~Attached_dataspace() { env()->rm_session()->detach(_local_addr); }
+
+		/**
+		 * Return capability of the used dataspace
+		 */
+		Dataspace_capability cap() const { return _ds; }
+
+		/**
+		 * Request local address
+		 *
+		 * This is a template to avoid inconvenient casts at the caller.
+		 * A newly attached dataspace is untyped memory anyway.
+		 */
+		template <typename T>
+		T *local_addr() { return static_cast<T *>(_local_addr); }
+
+		/**
+		 * Return size
+		 */
+		size_t size() const { return _size; }
+};
+
+#endif /* _INCLUDE__OS__ATTACHED_DATASPACE_H_ */
diff --git a/os/include/os/attached_rom_dataspace.h b/os/include/os/attached_rom_dataspace.h
index cebdc458d4..f86182b4a7 100644
--- a/os/include/os/attached_rom_dataspace.h
+++ b/os/include/os/attached_rom_dataspace.h
@@ -14,93 +14,65 @@
 #ifndef _INCLUDE__OS__ATTACHED_ROM_DATASPACE_H_
 #define _INCLUDE__OS__ATTACHED_ROM_DATASPACE_H_
 
+#include <util/volatile_object.h>
+#include <os/attached_dataspace.h>
 #include <rom_session/connection.h>
-#include <dataspace/client.h>
-#include <base/env.h>
 
-namespace Genode {
+namespace Genode { class Attached_rom_dataspace; }
 
-	class Attached_rom_dataspace
-	{
-		private:
 
-			Rom_connection            _rom;
-			Rom_dataspace_capability  _ds;
-			size_t                    _size;
-			void                     *_local_addr = 0;
+class Genode::Attached_rom_dataspace
+{
+	private:
 
-			void _detach()
-			{
-				if (!_local_addr)
-					return;
+		Rom_connection _rom;
 
-				env()->rm_session()->detach(_local_addr);
-				_local_addr = 0;
-				_size       = 0;
-			}
+		/*
+		 * A ROM module may change or disappear over the lifetime of a ROM
+		 * session. In contrast to the plain 'Attached_dataspace', which is
+		 * always be valid once constructed, a 'Attached_rom_dataspace' has
+		 * to handle the validity of the dataspace.
+		 */
+		Lazy_volatile_object<Attached_dataspace> _ds;
 
-			void _attach()
-			{
-				if (_local_addr)
-					_detach();
+		/**
+		 * Try to attach the ROM module, ignore invalid dataspaces
+		 */
+		void _try_attach()
+		{
+			try { _ds.construct(_rom.dataspace()); }
+			catch (Attached_dataspace::Invalid_dataspace) { }
+		}
 
-				_ds = _rom.dataspace();
-				if (_ds.valid()) {
-					_size       = Dataspace_client(_ds).size();
-					_local_addr = env()->rm_session()->attach(_ds);
-				}
-			}
+	public:
 
-		public:
+		/**
+		 * Constructor
+		 *
+		 * \throw Rom_connection::Rom_connection_failed
+		 * \throw Rm_session::Attach_failed
+		 */
+		Attached_rom_dataspace(char const *name)
+		: _rom(name) { _try_attach(); }
 
-			/**
-			 * Constructor
-			 *
-			 * \throw Rom_connection::Rom_connection_failed
-			 * \throw Rm_session::Attach_failed
-			 */
-			Attached_rom_dataspace(char const *name)
-			: _rom(name) { _attach(); }
+		template <typename T> T *local_addr() { return _ds->local_addr<T>(); }
 
-			/**
-			 * Destructor
-			 */
-			~Attached_rom_dataspace() { _detach(); }
+		size_t size() const { return _ds->size(); }
 
-			/**
-			 * Return capability of the used ROM dataspace
-			 */
-			Rom_dataspace_capability cap() const { return _ds; }
+		/**
+		 * Register signal handler for ROM module changes
+		 */
+		void sigh(Signal_context_capability sigh) { _rom.sigh(sigh); }
 
-			/**
-			 * Request local address
-			 *
-			 * This is a template to avoid inconvenient casts at the caller.
-			 * A newly allocated ROM dataspace is untyped memory anyway.
-			 */
-			template <typename T>
-			T *local_addr() { return static_cast<T *>(_local_addr); }
+		/**
+		 * Re-attach ROM module
+		 */
+		void update() { _try_attach(); }
 
-			/**
-			 * Return size
-			 */
-			size_t size() const { return _size; }
-
-			/**
-			 * Register signal handler for ROM module changes
-			 */
-			void sigh(Signal_context_capability sigh) { _rom.sigh(sigh); }
-
-			/**
-			 * Re-attach ROM module
-			 */
-			void update() { _attach(); }
-
-			/**
-			 * Return true of content is present
-			 */
-			bool is_valid() const { return _local_addr != 0; }
-	};
-}
+		/**
+		 * Return true of content is present
+		 */
+		bool is_valid() const { return _ds.is_constructed(); }
+};
 
 #endif /* _INCLUDE__OS__ATTACHED_ROM_DATASPACE_H_ */
diff --git a/os/include/terminal_session/client.h b/os/include/terminal_session/client.h
index 7d8b5212a1..32b868acae 100644
--- a/os/include/terminal_session/client.h
+++ b/os/include/terminal_session/client.h
@@ -18,103 +18,86 @@
 #include <util/misc_math.h>
 #include <util/string.h>
 #include <base/lock.h>
-#include <base/env.h>
 #include <base/rpc_client.h>
+#include <os/attached_dataspace.h>
 
 #include <terminal_session/terminal_session.h>
 
-namespace Terminal {
+namespace Terminal { class Session_client; }
 
-	class Session_client : public Genode::Rpc_client<Session>
-	{
-		private:
 
-			/**
-			 * Shared-memory buffer used for carrying the payload
-			 * of read/write operations
-			 */
-			struct Io_buffer
-			{
-				Genode::Dataspace_capability ds_cap;
-				char                        *base;
-				Genode::size_t               size;
-				Genode::Lock                 lock;
+class Terminal::Session_client : public Genode::Rpc_client<Session>
+{
+	private:
 
-				Io_buffer(Genode::Dataspace_capability ds_cap)
-				:
-					ds_cap(ds_cap),
-					base(Genode::env()->rm_session()->attach(ds_cap)),
-					size(ds_cap.call<Genode::Dataspace::Rpc_size>())
-				{ }
+		Genode::Lock _lock;
 
-				~Io_buffer()
-				{
-					Genode::env()->rm_session()->detach(base);
-				}
-			};
+		/**
+		 * Shared-memory buffer used for carrying the payload
+		 * of read/write operations
+		 */
+		Genode::Attached_dataspace _io_buffer;
 
-			Io_buffer _io_buffer;
+	public:
 
-		public:
+		Session_client(Genode::Capability<Session> cap)
+		:
+			Genode::Rpc_client<Session>(cap),
+			_io_buffer(call<Rpc_dataspace>())
+		{ }
 
-			Session_client(Genode::Capability<Session> cap)
-			:
-				Genode::Rpc_client<Session>(cap),
-				_io_buffer(call<Rpc_dataspace>())
-			{ }
+		Size size() { return call<Rpc_size>(); }
 
-			Size size() { return call<Rpc_size>(); }
+		bool avail() { return call<Rpc_avail>(); }
 
-			bool avail() { return call<Rpc_avail>(); }
+		Genode::size_t read(void *buf, Genode::size_t buf_size)
+		{
+			Genode::Lock::Guard _guard(_lock);
 
-			Genode::size_t read(void *buf, Genode::size_t buf_size)
-			{
-				Genode::Lock::Guard _guard(_io_buffer.lock);
+			/* instruct server to fill the I/O buffer */
+			Genode::size_t num_bytes = call<Rpc_read>(buf_size);
 
-				/* instruct server to fill the I/O buffer */
-				Genode::size_t num_bytes = call<Rpc_read>(buf_size);
+			/* copy-out I/O buffer */
+			num_bytes = Genode::min(num_bytes, buf_size);
+			Genode::memcpy(buf, _io_buffer.local_addr<char>(), num_bytes);
 
-				/* copy-out I/O buffer */
-				num_bytes = Genode::min(num_bytes, buf_size);
-				Genode::memcpy(buf, _io_buffer.base, num_bytes);
+			return num_bytes;
+		}
 
-				return num_bytes;
+		Genode::size_t write(void const *buf, Genode::size_t num_bytes)
+		{
+			Genode::Lock::Guard _guard(_lock);
+
+			Genode::size_t     written_bytes = 0;
+			char const * const src           = (char const *)buf;
+
+			while (written_bytes < num_bytes) {
+
+				/* copy payload to I/O buffer */
+				Genode::size_t n = Genode::min(num_bytes - written_bytes,
+				                               _io_buffer.size());
+				Genode::memcpy(_io_buffer.local_addr<char>(),
+				               src + written_bytes, n);
+
+				/* tell server to pick up new I/O buffer content */
+				call<Rpc_write>(n);
+
+				written_bytes += n;
 			}
+			return num_bytes;
+		}
 
-			Genode::size_t write(void const *buf, Genode::size_t num_bytes)
-			{
-				Genode::Lock::Guard _guard(_io_buffer.lock);
+		void connected_sigh(Genode::Signal_context_capability cap)
+		{
+			call<Rpc_connected_sigh>(cap);
+		}
 
-				Genode::size_t     written_bytes = 0;
-				char const * const src           = (char const *)buf;
+		void read_avail_sigh(Genode::Signal_context_capability cap)
+		{
+			call<Rpc_read_avail_sigh>(cap);
+		}
 
-				while (written_bytes < num_bytes) {
-
-					/* copy payload to I/O buffer */
-					Genode::size_t n = Genode::min(num_bytes - written_bytes,
-					                               _io_buffer.size);
-					Genode::memcpy(_io_buffer.base, src + written_bytes, n);
-
-					/* tell server to pick up new I/O buffer content */
-					call<Rpc_write>(n);
-
-					written_bytes += n;
-				}
-				return num_bytes;
-			}
-
-			void connected_sigh(Genode::Signal_context_capability cap)
-			{
-				call<Rpc_connected_sigh>(cap);
-			}
-
-			void read_avail_sigh(Genode::Signal_context_capability cap)
-			{
-				call<Rpc_read_avail_sigh>(cap);
-			}
-
-			Genode::size_t io_buffer_size() const { return _io_buffer.size; }
-	};
-}
+		Genode::size_t io_buffer_size() const { return _io_buffer.size(); }
+};
 
 #endif /* _INCLUDE__TERMINAL_SESSION__CLIENT_H_ */